From ccc87a03f0ec70eb28f9ce49221834718abeca7d Mon Sep 17 00:00:00 2001 From: Misi Date: Fri, 12 Sep 2025 17:15:15 +0200 Subject: [PATCH 01/33] Fix: Fix redirection after login when Grafana is served from subpath (#110889) Fix short link (/goto) redirection when Grafana is served from subpath --- public/app/app.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/public/app/app.ts b/public/app/app.ts index 05f6a2e0ab0..8269bb6a2d7 100644 --- a/public/app/app.ts +++ b/public/app/app.ts @@ -472,14 +472,18 @@ function handleRedirectTo(): void { } window.sessionStorage.removeItem(RedirectToUrlKey); - const decodedRedirectTo = decodeURIComponent(redirectTo); + let decodedRedirectTo = decodeURIComponent(redirectTo); if (decodedRedirectTo.startsWith('/goto/')) { // In this case there should be a request to the backend + if (config.appSubUrl && !decodedRedirectTo.startsWith(config.appSubUrl)) { + decodedRedirectTo = config.appSubUrl + decodedRedirectTo; + } window.location.replace(decodedRedirectTo); - } else { - const stripped = locationUtil.stripBaseFromUrl(decodedRedirectTo); - locationService.replace(stripped); + return; } + // Ensure that the appsuburl is stripped from the redirect to in case of a frontend redirect + const stripped = locationUtil.stripBaseFromUrl(decodedRedirectTo); + locationService.replace(stripped); } export default new GrafanaApp(); From de01b3e2092ce0f4cabb8920387d0671dd1ed3ec Mon Sep 17 00:00:00 2001 From: Haris Rozajac <58232930+harisrozajac@users.noreply.github.com> Date: Fri, 12 Sep 2025 11:31:05 -0600 Subject: [PATCH 02/33] Dashboard Schema V2: Support panel actions (#110842) * support panel actions * refactor * add test; move action transformer to utils * refactor so v2 headers and queryParams are just a simple record * update open api * update actions to be same shape accross all dashboard schemas and add validation on the backend * cleanup * update snapshot * add tests to validation --- .../kinds/v2alpha1/dashboard_spec.cue | 44 ++ .../kinds/v2beta1/dashboard_spec.cue | 45 ++ .../dashboard/v0alpha1/dashboard_kind.cue | 56 ++ .../apis/dashboard/v1beta1/dashboard_kind.cue | 56 ++ .../dashboard/v2alpha1/dashboard_spec.cue | 44 ++ .../dashboard/v2alpha1/dashboard_spec_gen.go | 101 ++++ .../pkg/apis/dashboard/v2alpha1/validation.go | 58 ++ .../dashboard/v2alpha1/validation_test.go | 495 ++++++++++++++++++ .../v2alpha1/zz_generated.openapi.go | 301 ++++++++++- ...enerated.openapi_violation_exceptions.list | 6 + .../apis/dashboard/v2beta1/dashboard_spec.cue | 45 ++ .../dashboard/v2beta1/dashboard_spec_gen.go | 101 ++++ .../pkg/apis/dashboard/v2beta1/validation.go | 58 ++ .../apis/dashboard/v2beta1/validation_test.go | 495 ++++++++++++++++++ .../dashboard/v2beta1/zz_generated.openapi.go | 301 ++++++++++- ...enerated.openapi_violation_exceptions.list | 6 + kinds/dashboard/dashboard_kind.cue | 56 ++ packages/grafana-schema/src/index.gen.ts | 10 + .../raw/dashboard/x/dashboard_types.gen.ts | 86 +++ .../dashboard/v2alpha1/types.spec.gen.ts | 77 +++ .../dashboard/v2beta1/types.spec.gen.ts | 77 +++ pkg/kinds/dashboard/dashboard_spec_gen.go | 97 ++++ .../dashboard.grafana.app-v2alpha1.json | 172 ++++++ .../transformToV2TypesUtils.test.ts | 46 +- 24 files changed, 2808 insertions(+), 25 deletions(-) create mode 100644 apps/dashboard/pkg/apis/dashboard/v2alpha1/validation_test.go create mode 100644 apps/dashboard/pkg/apis/dashboard/v2beta1/validation_test.go diff --git a/apps/dashboard/kinds/v2alpha1/dashboard_spec.cue b/apps/dashboard/kinds/v2alpha1/dashboard_spec.cue index 2aba16b9489..6bad69b84a9 100644 --- a/apps/dashboard/kinds/v2alpha1/dashboard_spec.cue +++ b/apps/dashboard/kinds/v2alpha1/dashboard_spec.cue @@ -221,6 +221,9 @@ FieldConfig: { // The behavior when clicking on a result links?: [...] + // Define interactive HTTP requests that can be triggered from data visualizations. + actions?: [...Action] + // Alternative to empty string noValue?: string @@ -364,6 +367,47 @@ FieldColor: { // Dashboard Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource) DashboardLinkType: "link" | "dashboards" +ActionType: "fetch" | "infinity" + +FetchOptions: { + method: HttpRequestMethod + url: string + body?: string + // These are 2D arrays of strings, each representing a key-value pair + // We are defining them this way because we can't generate a go struct that + // that would have exactly two strings in each sub-array + queryParams?: [...[...string]] + headers?: [...[...string]] +} + +InfinityOptions: FetchOptions & { + datasourceUid: string +} + +HttpRequestMethod: "GET" | "PUT" | "POST" | "DELETE" | "PATCH" + +// Action variable type +ActionVariableType: "string" + +ActionVariable: { + key: string + name: string + type: ActionVariableType +} + +Action: { + type: ActionType + title: string + fetch?: FetchOptions + infinity?: InfinityOptions + confirmation?: string + oneClick?: bool + variables?: [...ActionVariable] + style?: { + backgroundColor?: string + } +} + // --- Common types --- Kind: { kind: string diff --git a/apps/dashboard/kinds/v2beta1/dashboard_spec.cue b/apps/dashboard/kinds/v2beta1/dashboard_spec.cue index 0e9dcee43b4..93a195a732e 100644 --- a/apps/dashboard/kinds/v2beta1/dashboard_spec.cue +++ b/apps/dashboard/kinds/v2beta1/dashboard_spec.cue @@ -219,6 +219,9 @@ FieldConfig: { // The behavior when clicking on a result links?: [...] + // Define interactive HTTP requests that can be triggered from data visualizations. + actions?: [...Action] + // Alternative to empty string noValue?: string @@ -362,6 +365,48 @@ FieldColor: { // Dashboard Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource) DashboardLinkType: "link" | "dashboards" +ActionType: "fetch" | "infinity" + +FetchOptions: { + method: HttpRequestMethod + url: string + body?: string + // These are 2D arrays of strings, each representing a key-value pair + // We are defining them this way because we can't generate a go struct that + // that would have exactly two strings in each sub-array + queryParams?: [...[...string]] + headers?: [...[...string]] +} + +InfinityOptions: FetchOptions & { + datasourceUid: string +} + +HttpRequestMethod: "GET" | "PUT" | "POST" | "DELETE" | "PATCH" + +// Action variable type +ActionVariableType: "string" + +ActionVariable: { + key: string + name: string + type: ActionVariableType +} + +Action: { + type: ActionType + title: string + fetch?: FetchOptions + infinity?: InfinityOptions + confirmation?: string + oneClick?: bool + variables?: [...ActionVariable] + style?: { + backgroundColor?: string + } +} + + // --- Common types --- Kind: { kind: string diff --git a/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_kind.cue b/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_kind.cue index 895a7bc946a..fb971b2cc3f 100644 --- a/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_kind.cue +++ b/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_kind.cue @@ -302,6 +302,59 @@ lineage: schemas: [{ // - "inControlsMenu" renders the link in bottom part of the dashboard controls dropdown menu #DashboardLinkPlacement: "inControlsMenu" @cuetsy(kind="type") + // Dashboard action type + #ActionType: "fetch" | "infinity" @cuetsy(kind="type") + + // Fetch options + #FetchOptions: { + method: #HttpRequestMethod + url: string + body?: string + // These are 2D arrays of strings, each representing a key-value pair + // We are defining this way because we can't generate a go struct that + // that would have exactly two strings in each sub-array + queryParams?: [...[...string]] + headers?: [...[...string]] + } @cuetsy(kind="interface") + + // Infinity options + #InfinityOptions: { + method: #HttpRequestMethod + url: string + body?: string + // These are 2D arrays of strings, each representing a key-value pair + // We are defining them this way because we can't generate a go struct that + // that would have exactly two strings in each sub-array + queryParams?: [...[...string]] + headers?: [...[...string]] + datasourceUid: string + } @cuetsy(kind="interface") + + #HttpRequestMethod: "GET" | "PUT" | "POST" | "DELETE" | "PATCH" @cuetsy(kind="type") + + // Action variable type + #ActionVariableType: "string" @cuetsy(kind="type") + + #ActionVariable: { + key: string + name: string + type: #ActionVariableType + } @cuetsy(kind="interface") + + // Dashboard action + #Action: { + type: #ActionType + title: string + fetch?: #FetchOptions + infinity?: #InfinityOptions + confirmation?: string + oneClick?: bool + variables?: [...#ActionVariable] + style?: { + backgroundColor?: string + } + } @cuetsy(kind="interface") + // 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). @@ -731,6 +784,9 @@ lineage: schemas: [{ // The behavior when clicking on a result links?: [...] @grafanamaturity(NeedsExpertReview) + // Define interactive HTTP requests that can be triggered from data visualizations. + actions?: [...#Action] @grafanamaturity(NeedsExpertReview) + // Alternative to empty string noValue?: string @grafanamaturity(NeedsExpertReview) diff --git a/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_kind.cue b/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_kind.cue index 895a7bc946a..fb971b2cc3f 100644 --- a/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_kind.cue +++ b/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_kind.cue @@ -302,6 +302,59 @@ lineage: schemas: [{ // - "inControlsMenu" renders the link in bottom part of the dashboard controls dropdown menu #DashboardLinkPlacement: "inControlsMenu" @cuetsy(kind="type") + // Dashboard action type + #ActionType: "fetch" | "infinity" @cuetsy(kind="type") + + // Fetch options + #FetchOptions: { + method: #HttpRequestMethod + url: string + body?: string + // These are 2D arrays of strings, each representing a key-value pair + // We are defining this way because we can't generate a go struct that + // that would have exactly two strings in each sub-array + queryParams?: [...[...string]] + headers?: [...[...string]] + } @cuetsy(kind="interface") + + // Infinity options + #InfinityOptions: { + method: #HttpRequestMethod + url: string + body?: string + // These are 2D arrays of strings, each representing a key-value pair + // We are defining them this way because we can't generate a go struct that + // that would have exactly two strings in each sub-array + queryParams?: [...[...string]] + headers?: [...[...string]] + datasourceUid: string + } @cuetsy(kind="interface") + + #HttpRequestMethod: "GET" | "PUT" | "POST" | "DELETE" | "PATCH" @cuetsy(kind="type") + + // Action variable type + #ActionVariableType: "string" @cuetsy(kind="type") + + #ActionVariable: { + key: string + name: string + type: #ActionVariableType + } @cuetsy(kind="interface") + + // Dashboard action + #Action: { + type: #ActionType + title: string + fetch?: #FetchOptions + infinity?: #InfinityOptions + confirmation?: string + oneClick?: bool + variables?: [...#ActionVariable] + style?: { + backgroundColor?: string + } + } @cuetsy(kind="interface") + // 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). @@ -731,6 +784,9 @@ lineage: schemas: [{ // The behavior when clicking on a result links?: [...] @grafanamaturity(NeedsExpertReview) + // Define interactive HTTP requests that can be triggered from data visualizations. + actions?: [...#Action] @grafanamaturity(NeedsExpertReview) + // Alternative to empty string noValue?: string @grafanamaturity(NeedsExpertReview) diff --git a/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec.cue b/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec.cue index f566d3775b6..602f639f81a 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec.cue +++ b/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec.cue @@ -225,6 +225,9 @@ FieldConfig: { // The behavior when clicking on a result links?: [...] + // Define interactive HTTP requests that can be triggered from data visualizations. + actions?: [...Action] + // Alternative to empty string noValue?: string @@ -368,6 +371,47 @@ FieldColor: { // Dashboard Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource) DashboardLinkType: "link" | "dashboards" +ActionType: "fetch" | "infinity" + +FetchOptions: { + method: HttpRequestMethod + url: string + body?: string + // These are 2D arrays of strings, each representing a key-value pair + // We are defining them this way because we can't generate a go struct that + // that would have exactly two strings in each sub-array + queryParams?: [...[...string]] + headers?: [...[...string]] +} + +InfinityOptions: FetchOptions & { + datasourceUid: string +} + +HttpRequestMethod: "GET" | "PUT" | "POST" | "DELETE" | "PATCH" + +// Action variable type +ActionVariableType: "string" + +ActionVariable: { + key: string + name: string + type: ActionVariableType +} + +Action: { + type: ActionType + title: string + fetch?: FetchOptions + infinity?: InfinityOptions + confirmation?: string + oneClick?: bool + variables?: [...ActionVariable] + style?: { + backgroundColor?: string + } +} + // --- Common types --- Kind: { kind: string diff --git a/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec_gen.go b/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec_gen.go index f1e36092724..3f4c7d9f1f5 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec_gen.go +++ b/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec_gen.go @@ -392,6 +392,8 @@ type DashboardFieldConfig struct { Color *DashboardFieldColor `json:"color,omitempty"` // The behavior when clicking on a result Links []interface{} `json:"links,omitempty"` + // Define interactive HTTP requests that can be triggered from data visualizations. + Actions []DashboardAction `json:"actions,omitempty"` // Alternative to empty string NoValue *string `json:"noValue,omitempty"` // custom is specified by the FieldConfig field @@ -623,6 +625,95 @@ const ( DashboardFieldColorSeriesByModeLast DashboardFieldColorSeriesByMode = "last" ) +// +k8s:openapi-gen=true +type DashboardAction struct { + Type DashboardActionType `json:"type"` + Title string `json:"title"` + Fetch *DashboardFetchOptions `json:"fetch,omitempty"` + Infinity *DashboardInfinityOptions `json:"infinity,omitempty"` + Confirmation *string `json:"confirmation,omitempty"` + OneClick *bool `json:"oneClick,omitempty"` + Variables []DashboardActionVariable `json:"variables,omitempty"` + Style *DashboardV2alpha1ActionStyle `json:"style,omitempty"` +} + +// NewDashboardAction creates a new DashboardAction object. +func NewDashboardAction() *DashboardAction { + return &DashboardAction{} +} + +// +k8s:openapi-gen=true +type DashboardActionType string + +const ( + DashboardActionTypeFetch DashboardActionType = "fetch" + DashboardActionTypeInfinity DashboardActionType = "infinity" +) + +// +k8s:openapi-gen=true +type DashboardFetchOptions struct { + Method DashboardHttpRequestMethod `json:"method"` + Url string `json:"url"` + Body *string `json:"body,omitempty"` + // These are 2D arrays of strings, each representing a key-value pair + // We are defining them this way because we can't generate a go struct that + // that would have exactly two strings in each sub-array + QueryParams [][]string `json:"queryParams,omitempty"` + Headers [][]string `json:"headers,omitempty"` +} + +// NewDashboardFetchOptions creates a new DashboardFetchOptions object. +func NewDashboardFetchOptions() *DashboardFetchOptions { + return &DashboardFetchOptions{} +} + +// +k8s:openapi-gen=true +type DashboardHttpRequestMethod string + +const ( + DashboardHttpRequestMethodGET DashboardHttpRequestMethod = "GET" + DashboardHttpRequestMethodPUT DashboardHttpRequestMethod = "PUT" + DashboardHttpRequestMethodPOST DashboardHttpRequestMethod = "POST" + DashboardHttpRequestMethodDELETE DashboardHttpRequestMethod = "DELETE" + DashboardHttpRequestMethodPATCH DashboardHttpRequestMethod = "PATCH" +) + +// +k8s:openapi-gen=true +type DashboardInfinityOptions struct { + Method DashboardHttpRequestMethod `json:"method"` + Url string `json:"url"` + Body *string `json:"body,omitempty"` + // These are 2D arrays of strings, each representing a key-value pair + // We are defining them this way because we can't generate a go struct that + // that would have exactly two strings in each sub-array + QueryParams [][]string `json:"queryParams,omitempty"` + DatasourceUid string `json:"datasourceUid"` + Headers [][]string `json:"headers,omitempty"` +} + +// NewDashboardInfinityOptions creates a new DashboardInfinityOptions object. +func NewDashboardInfinityOptions() *DashboardInfinityOptions { + return &DashboardInfinityOptions{} +} + +// +k8s:openapi-gen=true +type DashboardActionVariable struct { + Key string `json:"key"` + Name string `json:"name"` + Type string `json:"type"` +} + +// NewDashboardActionVariable creates a new DashboardActionVariable object. +func NewDashboardActionVariable() *DashboardActionVariable { + return &DashboardActionVariable{ + Type: DashboardActionVariableType, + } +} + +// Action variable type +// +k8s:openapi-gen=true +const DashboardActionVariableType = "string" + // +k8s:openapi-gen=true type DashboardDynamicConfigValue struct { Id string `json:"id"` @@ -1831,6 +1922,16 @@ func NewDashboardV2alpha1SpecialValueMapOptions() *DashboardV2alpha1SpecialValue } } +// +k8s:openapi-gen=true +type DashboardV2alpha1ActionStyle struct { + BackgroundColor *string `json:"backgroundColor,omitempty"` +} + +// NewDashboardV2alpha1ActionStyle creates a new DashboardV2alpha1ActionStyle object. +func NewDashboardV2alpha1ActionStyle() *DashboardV2alpha1ActionStyle { + return &DashboardV2alpha1ActionStyle{} +} + // +k8s:openapi-gen=true type DashboardRepeatOptionsDirection string diff --git a/apps/dashboard/pkg/apis/dashboard/v2alpha1/validation.go b/apps/dashboard/pkg/apis/dashboard/v2alpha1/validation.go index 7445000d7f8..7c61faa8924 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2alpha1/validation.go +++ b/apps/dashboard/pkg/apis/dashboard/v2alpha1/validation.go @@ -23,6 +23,9 @@ func ValidateDashboardSpec(obj *Dashboard) field.ErrorList { } } + // Custom validation for action query params and headers + validateAndTrimActionArrays(obj) + if err := cuejson.Validate(data, getCueSchema()); err != nil { errs := field.ErrorList{} @@ -60,6 +63,61 @@ func ValidateDashboardSpec(obj *Dashboard) field.ErrorList { return nil } +// Validates and trims action query params and headers to exactly 2 elements each +// This is because we couldn't generate with cue a go struct that would have exactly two strings in each sub-array +func validateAndTrimActionArrays(obj *Dashboard) { + for _, element := range obj.Spec.Elements { + if element.PanelKind != nil { + panelElement := element.PanelKind + if panelElement.Spec.VizConfig.Spec.FieldConfig.Defaults.Actions != nil { + processActions(panelElement.Spec.VizConfig.Spec.FieldConfig.Defaults.Actions) + } + } + } +} + +// Helper function to process action arrays +func processActions(actions []DashboardAction) { + for _, action := range actions { + // Process FetchOptions if present + if action.Fetch != nil { + if action.Fetch.QueryParams != nil { + action.Fetch.QueryParams = trimStringArrays(action.Fetch.QueryParams) + } + if action.Fetch.Headers != nil { + action.Fetch.Headers = trimStringArrays(action.Fetch.Headers) + } + } + + // Process InfinityOptions if present + if action.Infinity != nil { + if action.Infinity.QueryParams != nil { + action.Infinity.QueryParams = trimStringArrays(action.Infinity.QueryParams) + } + if action.Infinity.Headers != nil { + action.Infinity.Headers = trimStringArrays(action.Infinity.Headers) + } + } + } +} + +// Helper function to trim 2D string arrays to exactly 2 elements per sub-array +func trimStringArrays(arrays [][]string) [][]string { + if arrays == nil { + return arrays + } + + result := make([][]string, len(arrays)) + for i, arr := range arrays { + if len(arr) > 2 { + result[i] = arr[:2] + } else { + result[i] = arr + } + } + return result +} + func formatErrorPath(path []string) string { return strings.Join(path, ".") } diff --git a/apps/dashboard/pkg/apis/dashboard/v2alpha1/validation_test.go b/apps/dashboard/pkg/apis/dashboard/v2alpha1/validation_test.go new file mode 100644 index 00000000000..1bac5cfa9e8 --- /dev/null +++ b/apps/dashboard/pkg/apis/dashboard/v2alpha1/validation_test.go @@ -0,0 +1,495 @@ +package v2alpha1 + +import ( + "testing" + + "github.com/stretchr/testify/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestTrimStringArrays(t *testing.T) { + tests := []struct { + name string + input [][]string + expected [][]string + }{ + { + name: "nil input", + input: nil, + expected: nil, + }, + { + name: "empty input", + input: [][]string{}, + expected: [][]string{}, + }, + { + name: "arrays with exactly 2 elements", + input: [][]string{{"key1", "value1"}, {"key2", "value2"}}, + expected: [][]string{{"key1", "value1"}, {"key2", "value2"}}, + }, + { + name: "arrays with less than 2 elements", + input: [][]string{{"key1"}, {}}, + expected: [][]string{{"key1"}, {}}, + }, + { + name: "arrays with more than 2 elements", + input: [][]string{{"key1", "value1", "extra1"}, {"key2", "value2", "extra2", "extra3"}}, + expected: [][]string{{"key1", "value1"}, {"key2", "value2"}}, + }, + { + name: "mixed arrays", + input: [][]string{{"key1"}, {"key2", "value2"}, {"key3", "value3", "extra"}}, + expected: [][]string{{"key1"}, {"key2", "value2"}, {"key3", "value3"}}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := trimStringArrays(tt.input) + assert.Equal(t, tt.expected, result) + }) + } +} + +func TestProcessActions(t *testing.T) { + tests := []struct { + name string + actions []DashboardAction + expected []DashboardAction + }{ + { + name: "empty actions", + actions: []DashboardAction{}, + expected: []DashboardAction{}, + }, + { + name: "action with fetch options having oversized arrays", + actions: []DashboardAction{ + { + Type: DashboardActionTypeFetch, + Title: "Test Fetch", + Fetch: &DashboardFetchOptions{ + Method: DashboardHttpRequestMethodGET, + Url: "http://example.com", + QueryParams: [][]string{ + {"param1", "value1", "extra1"}, + {"param2", "value2"}, + }, + Headers: [][]string{ + {"header1", "value1", "extra1", "extra2"}, + {"header2", "value2"}, + }, + }, + }, + }, + expected: []DashboardAction{ + { + Type: DashboardActionTypeFetch, + Title: "Test Fetch", + Fetch: &DashboardFetchOptions{ + Method: DashboardHttpRequestMethodGET, + Url: "http://example.com", + QueryParams: [][]string{ + {"param1", "value1"}, + {"param2", "value2"}, + }, + Headers: [][]string{ + {"header1", "value1"}, + {"header2", "value2"}, + }, + }, + }, + }, + }, + { + name: "action with infinity options having oversized arrays", + actions: []DashboardAction{ + { + Type: DashboardActionTypeInfinity, + Title: "Test Infinity", + Infinity: &DashboardInfinityOptions{ + Method: DashboardHttpRequestMethodPOST, + Url: "http://example.com", + DatasourceUid: "test-uid", + QueryParams: [][]string{ + {"param1", "value1", "extra1"}, + {"param2", "value2"}, + }, + Headers: [][]string{ + {"header1", "value1", "extra1", "extra2"}, + {"header2", "value2"}, + }, + }, + }, + }, + expected: []DashboardAction{ + { + Type: DashboardActionTypeInfinity, + Title: "Test Infinity", + Infinity: &DashboardInfinityOptions{ + Method: DashboardHttpRequestMethodPOST, + Url: "http://example.com", + DatasourceUid: "test-uid", + QueryParams: [][]string{ + {"param1", "value1"}, + {"param2", "value2"}, + }, + Headers: [][]string{ + {"header1", "value1"}, + {"header2", "value2"}, + }, + }, + }, + }, + }, + { + name: "action without fetch or infinity options", + actions: []DashboardAction{ + { + Type: DashboardActionTypeFetch, + Title: "Test Action", + }, + }, + expected: []DashboardAction{ + { + Type: DashboardActionTypeFetch, + Title: "Test Action", + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Create a copy to avoid modifying the original + actionsCopy := make([]DashboardAction, len(tt.actions)) + for i, action := range tt.actions { + actionsCopy[i] = action + // Deep copy the fetch options if they exist + if action.Fetch != nil { + fetchCopy := *action.Fetch + if action.Fetch.QueryParams != nil { + fetchCopy.QueryParams = make([][]string, len(action.Fetch.QueryParams)) + for j, param := range action.Fetch.QueryParams { + fetchCopy.QueryParams[j] = make([]string, len(param)) + copy(fetchCopy.QueryParams[j], param) + } + } + if action.Fetch.Headers != nil { + fetchCopy.Headers = make([][]string, len(action.Fetch.Headers)) + for j, header := range action.Fetch.Headers { + fetchCopy.Headers[j] = make([]string, len(header)) + copy(fetchCopy.Headers[j], header) + } + } + actionsCopy[i].Fetch = &fetchCopy + } + // Deep copy the infinity options if they exist + if action.Infinity != nil { + infinityCopy := *action.Infinity + if action.Infinity.QueryParams != nil { + infinityCopy.QueryParams = make([][]string, len(action.Infinity.QueryParams)) + for j, param := range action.Infinity.QueryParams { + infinityCopy.QueryParams[j] = make([]string, len(param)) + copy(infinityCopy.QueryParams[j], param) + } + } + if action.Infinity.Headers != nil { + infinityCopy.Headers = make([][]string, len(action.Infinity.Headers)) + for j, header := range action.Infinity.Headers { + infinityCopy.Headers[j] = make([]string, len(header)) + copy(infinityCopy.Headers[j], header) + } + } + actionsCopy[i].Infinity = &infinityCopy + } + } + + processActions(actionsCopy) + assert.Equal(t, tt.expected, actionsCopy) + }) + } +} + +func TestValidateAndTrimActionArrays(t *testing.T) { + tests := []struct { + name string + dashboard *Dashboard + expected *Dashboard + }{ + { + name: "dashboard with no elements", + dashboard: &Dashboard{ + TypeMeta: metav1.TypeMeta{ + Kind: "Dashboard", + APIVersion: "v2beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-dashboard", + }, + Spec: DashboardSpec{ + Elements: map[string]DashboardElement{}, + }, + }, + expected: &Dashboard{ + TypeMeta: metav1.TypeMeta{ + Kind: "Dashboard", + APIVersion: "v2beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-dashboard", + }, + Spec: DashboardSpec{ + Elements: map[string]DashboardElement{}, + }, + }, + }, + { + name: "dashboard with panel having actions with oversized arrays", + dashboard: &Dashboard{ + TypeMeta: metav1.TypeMeta{ + Kind: "Dashboard", + APIVersion: "v2beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-dashboard", + }, + Spec: DashboardSpec{ + Elements: map[string]DashboardElement{ + "panel1": { + PanelKind: &DashboardPanelKind{ + Spec: DashboardPanelSpec{ + VizConfig: DashboardVizConfigKind{ + Spec: DashboardVizConfigSpec{ + FieldConfig: DashboardFieldConfigSource{ + Defaults: DashboardFieldConfig{ + Actions: []DashboardAction{ + { + Type: DashboardActionTypeFetch, + Title: "Test Action", + Fetch: &DashboardFetchOptions{ + Method: DashboardHttpRequestMethodGET, + Url: "http://example.com", + QueryParams: [][]string{ + {"param1", "value1", "extra1"}, + {"param2", "value2"}, + }, + Headers: [][]string{ + {"header1", "value1", "extra1", "extra2"}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + expected: &Dashboard{ + TypeMeta: metav1.TypeMeta{ + Kind: "Dashboard", + APIVersion: "v2beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-dashboard", + }, + Spec: DashboardSpec{ + Elements: map[string]DashboardElement{ + "panel1": { + PanelKind: &DashboardPanelKind{ + Spec: DashboardPanelSpec{ + VizConfig: DashboardVizConfigKind{ + Spec: DashboardVizConfigSpec{ + FieldConfig: DashboardFieldConfigSource{ + Defaults: DashboardFieldConfig{ + Actions: []DashboardAction{ + { + Type: DashboardActionTypeFetch, + Title: "Test Action", + Fetch: &DashboardFetchOptions{ + Method: DashboardHttpRequestMethodGET, + Url: "http://example.com", + QueryParams: [][]string{ + {"param1", "value1"}, + {"param2", "value2"}, + }, + Headers: [][]string{ + {"header1", "value1"}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "dashboard with panel having no actions", + dashboard: &Dashboard{ + TypeMeta: metav1.TypeMeta{ + Kind: "Dashboard", + APIVersion: "v2beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-dashboard", + }, + Spec: DashboardSpec{ + Elements: map[string]DashboardElement{ + "panel1": { + PanelKind: &DashboardPanelKind{ + Spec: DashboardPanelSpec{ + VizConfig: DashboardVizConfigKind{ + Spec: DashboardVizConfigSpec{ + FieldConfig: DashboardFieldConfigSource{ + Defaults: DashboardFieldConfig{ + Actions: nil, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + expected: &Dashboard{ + TypeMeta: metav1.TypeMeta{ + Kind: "Dashboard", + APIVersion: "v2beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-dashboard", + }, + Spec: DashboardSpec{ + Elements: map[string]DashboardElement{ + "panel1": { + PanelKind: &DashboardPanelKind{ + Spec: DashboardPanelSpec{ + VizConfig: DashboardVizConfigKind{ + Spec: DashboardVizConfigSpec{ + FieldConfig: DashboardFieldConfigSource{ + Defaults: DashboardFieldConfig{ + Actions: nil, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Create a deep copy to avoid modifying the original + dashboardCopy := deepCopyDashboard(tt.dashboard) + + validateAndTrimActionArrays(dashboardCopy) + assert.Equal(t, tt.expected, dashboardCopy) + }) + } +} + +// Helper function to create a deep copy of a Dashboard for testing +func deepCopyDashboard(original *Dashboard) *Dashboard { + if original == nil { + return nil + } + + result := &Dashboard{ + TypeMeta: original.TypeMeta, + ObjectMeta: original.ObjectMeta, + Spec: DashboardSpec{ + Elements: make(map[string]DashboardElement), + }, + Status: original.Status, + } + + for key, element := range original.Spec.Elements { + elementCopy := element + + if element.PanelKind != nil { + panelCopy := *element.PanelKind + elementCopy.PanelKind = &panelCopy + + if element.PanelKind.Spec.VizConfig.Spec.FieldConfig.Defaults.Actions != nil { + actions := element.PanelKind.Spec.VizConfig.Spec.FieldConfig.Defaults.Actions + actionsCopy := make([]DashboardAction, len(actions)) + + for j, action := range actions { + actionsCopy[j] = action + + // Deep copy fetch options + if action.Fetch != nil { + fetchCopy := *action.Fetch + if action.Fetch.QueryParams != nil { + fetchCopy.QueryParams = make([][]string, len(action.Fetch.QueryParams)) + for k, param := range action.Fetch.QueryParams { + fetchCopy.QueryParams[k] = make([]string, len(param)) + copy(fetchCopy.QueryParams[k], param) + } + } + if action.Fetch.Headers != nil { + fetchCopy.Headers = make([][]string, len(action.Fetch.Headers)) + for k, header := range action.Fetch.Headers { + fetchCopy.Headers[k] = make([]string, len(header)) + copy(fetchCopy.Headers[k], header) + } + } + actionsCopy[j].Fetch = &fetchCopy + } + + // Deep copy infinity options + if action.Infinity != nil { + infinityCopy := *action.Infinity + if action.Infinity.QueryParams != nil { + infinityCopy.QueryParams = make([][]string, len(action.Infinity.QueryParams)) + for k, param := range action.Infinity.QueryParams { + infinityCopy.QueryParams[k] = make([]string, len(param)) + copy(infinityCopy.QueryParams[k], param) + } + } + if action.Infinity.Headers != nil { + infinityCopy.Headers = make([][]string, len(action.Infinity.Headers)) + for k, header := range action.Infinity.Headers { + infinityCopy.Headers[k] = make([]string, len(header)) + copy(infinityCopy.Headers[k], header) + } + } + actionsCopy[j].Infinity = &infinityCopy + } + } + + elementCopy.PanelKind.Spec.VizConfig.Spec.FieldConfig.Defaults.Actions = actionsCopy + } + } + + if element.LibraryPanelKind != nil { + libraryCopy := *element.LibraryPanelKind + elementCopy.LibraryPanelKind = &libraryCopy + } + + result.Spec.Elements[key] = elementCopy + } + + return result +} diff --git a/apps/dashboard/pkg/apis/dashboard/v2alpha1/zz_generated.openapi.go b/apps/dashboard/pkg/apis/dashboard/v2alpha1/zz_generated.openapi.go index a53ba6483cb..0d2d96af8bc 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2alpha1/zz_generated.openapi.go +++ b/apps/dashboard/pkg/apis/dashboard/v2alpha1/zz_generated.openapi.go @@ -18,6 +18,8 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.AnnotationPermission": schema_pkg_apis_dashboard_v2alpha1_AnnotationPermission(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.Dashboard": schema_pkg_apis_dashboard_v2alpha1_Dashboard(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardAccess": schema_pkg_apis_dashboard_v2alpha1_DashboardAccess(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardAction": schema_pkg_apis_dashboard_v2alpha1_DashboardAction(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardActionVariable": schema_pkg_apis_dashboard_v2alpha1_DashboardActionVariable(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardAdHocFilterWithLabels": schema_pkg_apis_dashboard_v2alpha1_DashboardAdHocFilterWithLabels(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardAdhocVariableKind": schema_pkg_apis_dashboard_v2alpha1_DashboardAdhocVariableKind(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardAdhocVariableSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardAdhocVariableSpec(ref), @@ -52,6 +54,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardDatasourceVariableSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardDatasourceVariableSpec(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardDynamicConfigValue": schema_pkg_apis_dashboard_v2alpha1_DashboardDynamicConfigValue(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardElementReference": schema_pkg_apis_dashboard_v2alpha1_DashboardElementReference(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardFetchOptions": schema_pkg_apis_dashboard_v2alpha1_DashboardFetchOptions(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardFieldColor": schema_pkg_apis_dashboard_v2alpha1_DashboardFieldColor(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardFieldConfig": schema_pkg_apis_dashboard_v2alpha1_DashboardFieldConfig(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardFieldConfigSource": schema_pkg_apis_dashboard_v2alpha1_DashboardFieldConfigSource(ref), @@ -63,6 +66,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutSpec(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardGroupByVariableKind": schema_pkg_apis_dashboard_v2alpha1_DashboardGroupByVariableKind(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardGroupByVariableSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardGroupByVariableSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardInfinityOptions": schema_pkg_apis_dashboard_v2alpha1_DashboardInfinityOptions(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardIntervalVariableKind": schema_pkg_apis_dashboard_v2alpha1_DashboardIntervalVariableKind(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardIntervalVariableSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardIntervalVariableSpec(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardJSONCodec": schema_pkg_apis_dashboard_v2alpha1_DashboardJSONCodec(ref), @@ -109,6 +113,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardTimeRangeOption": schema_pkg_apis_dashboard_v2alpha1_DashboardTimeRangeOption(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardTimeSettingsSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardTimeSettingsSpec(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardTransformationKind": schema_pkg_apis_dashboard_v2alpha1_DashboardTransformationKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardV2alpha1ActionStyle": schema_pkg_apis_dashboard_v2alpha1_DashboardV2alpha1ActionStyle(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardV2alpha1FieldConfigSourceOverrides": schema_pkg_apis_dashboard_v2alpha1_DashboardV2alpha1FieldConfigSourceOverrides(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardV2alpha1RangeMapOptions": schema_pkg_apis_dashboard_v2alpha1_DashboardV2alpha1RangeMapOptions(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardV2alpha1RegexMapOptions": schema_pkg_apis_dashboard_v2alpha1_DashboardV2alpha1RegexMapOptions(ref), @@ -309,6 +314,109 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardAccess(ref common.ReferenceCall } } +func schema_pkg_apis_dashboard_v2alpha1_DashboardAction(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "title": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "fetch": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardFetchOptions"), + }, + }, + "infinity": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardInfinityOptions"), + }, + }, + "confirmation": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "oneClick": { + SchemaProps: spec.SchemaProps{ + Type: []string{"boolean"}, + Format: "", + }, + }, + "variables": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardActionVariable"), + }, + }, + }, + }, + }, + "style": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardV2alpha1ActionStyle"), + }, + }, + }, + Required: []string{"type", "title"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardActionVariable", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardFetchOptions", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardInfinityOptions", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardV2alpha1ActionStyle"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardActionVariable(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "key": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "type": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"key", "name", "type"}, + }, + }, + } +} + func schema_pkg_apis_dashboard_v2alpha1_DashboardAdHocFilterWithLabels(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -1805,6 +1913,82 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardElementReference(ref common.Ref } } +func schema_pkg_apis_dashboard_v2alpha1_DashboardFetchOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "method": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "url": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "body": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "queryParams": { + SchemaProps: spec.SchemaProps{ + Description: "These are 2D arrays of strings, each representing a key-value pair We are defining them this way because we can't generate a go struct that that would have exactly two strings in each sub-array", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + "headers": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + }, + Required: []string{"method", "url"}, + }, + }, + } +} + func schema_pkg_apis_dashboard_v2alpha1_DashboardFieldColor(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -1957,6 +2141,20 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardFieldConfig(ref common.Referenc }, }, }, + "actions": { + SchemaProps: spec.SchemaProps{ + Description: "Define interactive HTTP requests that can be triggered from data visualizations.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardAction"), + }, + }, + }, + }, + }, "noValue": { SchemaProps: spec.SchemaProps{ Description: "Alternative to empty string", @@ -1983,7 +2181,7 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardFieldConfig(ref common.Referenc }, }, Dependencies: []string{ - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardFieldColor", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardThresholdsConfig", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap"}, + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardAction", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardFieldColor", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardThresholdsConfig", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap"}, } } @@ -2345,6 +2543,89 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardGroupByVariableSpec(ref common. } } +func schema_pkg_apis_dashboard_v2alpha1_DashboardInfinityOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "method": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "url": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "body": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "queryParams": { + SchemaProps: spec.SchemaProps{ + Description: "These are 2D arrays of strings, each representing a key-value pair We are defining them this way because we can't generate a go struct that that would have exactly two strings in each sub-array", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + "datasourceUid": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "headers": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + }, + Required: []string{"method", "url", "datasourceUid"}, + }, + }, + } +} + func schema_pkg_apis_dashboard_v2alpha1_DashboardIntervalVariableKind(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -4337,6 +4618,24 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardTransformationKind(ref common.R } } +func schema_pkg_apis_dashboard_v2alpha1_DashboardV2alpha1ActionStyle(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "backgroundColor": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + func schema_pkg_apis_dashboard_v2alpha1_DashboardV2alpha1FieldConfigSourceOverrides(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/apps/dashboard/pkg/apis/dashboard/v2alpha1/zz_generated.openapi_violation_exceptions.list b/apps/dashboard/pkg/apis/dashboard/v2alpha1/zz_generated.openapi_violation_exceptions.list index 95eb59de600..84157df6895 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2alpha1/zz_generated.openapi_violation_exceptions.list +++ b/apps/dashboard/pkg/apis/dashboard/v2alpha1/zz_generated.openapi_violation_exceptions.list @@ -1,3 +1,4 @@ +API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardAction,Variables API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardAdHocFilterWithLabels,ValueLabels API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardAdHocFilterWithLabels,Values API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardAdhocVariableSpec,BaseFilters @@ -9,11 +10,16 @@ API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/ API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardCustomVariableSpec,Options API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardDashboardLink,Tags API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardDatasourceVariableSpec,Options +API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardFetchOptions,Headers +API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardFetchOptions,QueryParams +API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardFieldConfig,Actions API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardFieldConfig,Links API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardFieldConfig,Mappings API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardFieldConfigSource,Overrides API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardGridLayoutSpec,Items API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardGroupByVariableSpec,Options +API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardInfinityOptions,Headers +API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardInfinityOptions,QueryParams API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardIntervalVariableSpec,Options API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardMetadata,Finalizers API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardPanelSpec,Links diff --git a/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec.cue b/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec.cue index 000383a069e..8e0ea0ef763 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec.cue +++ b/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec.cue @@ -223,6 +223,9 @@ FieldConfig: { // The behavior when clicking on a result links?: [...] + // Define interactive HTTP requests that can be triggered from data visualizations. + actions?: [...Action] + // Alternative to empty string noValue?: string @@ -366,6 +369,48 @@ FieldColor: { // Dashboard Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource) DashboardLinkType: "link" | "dashboards" +ActionType: "fetch" | "infinity" + +FetchOptions: { + method: HttpRequestMethod + url: string + body?: string + // These are 2D arrays of strings, each representing a key-value pair + // We are defining them this way because we can't generate a go struct that + // that would have exactly two strings in each sub-array + queryParams?: [...[...string]] + headers?: [...[...string]] +} + +InfinityOptions: FetchOptions & { + datasourceUid: string +} + +HttpRequestMethod: "GET" | "PUT" | "POST" | "DELETE" | "PATCH" + +// Action variable type +ActionVariableType: "string" + +ActionVariable: { + key: string + name: string + type: ActionVariableType +} + +Action: { + type: ActionType + title: string + fetch?: FetchOptions + infinity?: InfinityOptions + confirmation?: string + oneClick?: bool + variables?: [...ActionVariable] + style?: { + backgroundColor?: string + } +} + + // --- Common types --- Kind: { kind: string diff --git a/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec_gen.go b/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec_gen.go index fedd4dfd20a..b94789abcbd 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec_gen.go +++ b/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec_gen.go @@ -386,6 +386,8 @@ type DashboardFieldConfig struct { Color *DashboardFieldColor `json:"color,omitempty"` // The behavior when clicking on a result Links []interface{} `json:"links,omitempty"` + // Define interactive HTTP requests that can be triggered from data visualizations. + Actions []DashboardAction `json:"actions,omitempty"` // Alternative to empty string NoValue *string `json:"noValue,omitempty"` // custom is specified by the FieldConfig field @@ -617,6 +619,95 @@ const ( DashboardFieldColorSeriesByModeLast DashboardFieldColorSeriesByMode = "last" ) +// +k8s:openapi-gen=true +type DashboardAction struct { + Type DashboardActionType `json:"type"` + Title string `json:"title"` + Fetch *DashboardFetchOptions `json:"fetch,omitempty"` + Infinity *DashboardInfinityOptions `json:"infinity,omitempty"` + Confirmation *string `json:"confirmation,omitempty"` + OneClick *bool `json:"oneClick,omitempty"` + Variables []DashboardActionVariable `json:"variables,omitempty"` + Style *DashboardV2beta1ActionStyle `json:"style,omitempty"` +} + +// NewDashboardAction creates a new DashboardAction object. +func NewDashboardAction() *DashboardAction { + return &DashboardAction{} +} + +// +k8s:openapi-gen=true +type DashboardActionType string + +const ( + DashboardActionTypeFetch DashboardActionType = "fetch" + DashboardActionTypeInfinity DashboardActionType = "infinity" +) + +// +k8s:openapi-gen=true +type DashboardFetchOptions struct { + Method DashboardHttpRequestMethod `json:"method"` + Url string `json:"url"` + Body *string `json:"body,omitempty"` + // These are 2D arrays of strings, each representing a key-value pair + // We are defining them this way because we can't generate a go struct that + // that would have exactly two strings in each sub-array + QueryParams [][]string `json:"queryParams,omitempty"` + Headers [][]string `json:"headers,omitempty"` +} + +// NewDashboardFetchOptions creates a new DashboardFetchOptions object. +func NewDashboardFetchOptions() *DashboardFetchOptions { + return &DashboardFetchOptions{} +} + +// +k8s:openapi-gen=true +type DashboardHttpRequestMethod string + +const ( + DashboardHttpRequestMethodGET DashboardHttpRequestMethod = "GET" + DashboardHttpRequestMethodPUT DashboardHttpRequestMethod = "PUT" + DashboardHttpRequestMethodPOST DashboardHttpRequestMethod = "POST" + DashboardHttpRequestMethodDELETE DashboardHttpRequestMethod = "DELETE" + DashboardHttpRequestMethodPATCH DashboardHttpRequestMethod = "PATCH" +) + +// +k8s:openapi-gen=true +type DashboardInfinityOptions struct { + Method DashboardHttpRequestMethod `json:"method"` + Url string `json:"url"` + Body *string `json:"body,omitempty"` + // These are 2D arrays of strings, each representing a key-value pair + // We are defining them this way because we can't generate a go struct that + // that would have exactly two strings in each sub-array + QueryParams [][]string `json:"queryParams,omitempty"` + DatasourceUid string `json:"datasourceUid"` + Headers [][]string `json:"headers,omitempty"` +} + +// NewDashboardInfinityOptions creates a new DashboardInfinityOptions object. +func NewDashboardInfinityOptions() *DashboardInfinityOptions { + return &DashboardInfinityOptions{} +} + +// +k8s:openapi-gen=true +type DashboardActionVariable struct { + Key string `json:"key"` + Name string `json:"name"` + Type string `json:"type"` +} + +// NewDashboardActionVariable creates a new DashboardActionVariable object. +func NewDashboardActionVariable() *DashboardActionVariable { + return &DashboardActionVariable{ + Type: DashboardActionVariableType, + } +} + +// Action variable type +// +k8s:openapi-gen=true +const DashboardActionVariableType = "string" + // +k8s:openapi-gen=true type DashboardDynamicConfigValue struct { Id string `json:"id"` @@ -1853,6 +1944,16 @@ func NewDashboardV2beta1SpecialValueMapOptions() *DashboardV2beta1SpecialValueMa } } +// +k8s:openapi-gen=true +type DashboardV2beta1ActionStyle struct { + BackgroundColor *string `json:"backgroundColor,omitempty"` +} + +// NewDashboardV2beta1ActionStyle creates a new DashboardV2beta1ActionStyle object. +func NewDashboardV2beta1ActionStyle() *DashboardV2beta1ActionStyle { + return &DashboardV2beta1ActionStyle{} +} + // +k8s:openapi-gen=true type DashboardV2beta1GroupByVariableKindDatasource struct { Name *string `json:"name,omitempty"` diff --git a/apps/dashboard/pkg/apis/dashboard/v2beta1/validation.go b/apps/dashboard/pkg/apis/dashboard/v2beta1/validation.go index 3946c0d9b14..7c859626b98 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2beta1/validation.go +++ b/apps/dashboard/pkg/apis/dashboard/v2beta1/validation.go @@ -23,6 +23,9 @@ func ValidateDashboardSpec(obj *Dashboard) field.ErrorList { } } + // Custom validation for action query params and headers + validateAndTrimActionArrays(obj) + if err := cuejson.Validate(data, getCueSchema()); err != nil { errs := field.ErrorList{} @@ -60,6 +63,61 @@ func ValidateDashboardSpec(obj *Dashboard) field.ErrorList { return nil } +// Validates and trims action query params and headers to exactly 2 elements each +// This is because we couldn't generate with cue a go struct that would have exactly two strings in each sub-array +func validateAndTrimActionArrays(obj *Dashboard) { + for _, element := range obj.Spec.Elements { + if element.PanelKind != nil { + panelElement := element.PanelKind + if panelElement.Spec.VizConfig.Spec.FieldConfig.Defaults.Actions != nil { + processActions(panelElement.Spec.VizConfig.Spec.FieldConfig.Defaults.Actions) + } + } + } +} + +// Helper function to process action arrays +func processActions(actions []DashboardAction) { + for _, action := range actions { + // Process FetchOptions if present + if action.Fetch != nil { + if action.Fetch.QueryParams != nil { + action.Fetch.QueryParams = trimStringArrays(action.Fetch.QueryParams) + } + if action.Fetch.Headers != nil { + action.Fetch.Headers = trimStringArrays(action.Fetch.Headers) + } + } + + // Process InfinityOptions if present + if action.Infinity != nil { + if action.Infinity.QueryParams != nil { + action.Infinity.QueryParams = trimStringArrays(action.Infinity.QueryParams) + } + if action.Infinity.Headers != nil { + action.Infinity.Headers = trimStringArrays(action.Infinity.Headers) + } + } + } +} + +// Helper function to trim 2D string arrays to exactly 2 elements per sub-array +func trimStringArrays(arrays [][]string) [][]string { + if arrays == nil { + return arrays + } + + result := make([][]string, len(arrays)) + for i, arr := range arrays { + if len(arr) > 2 { + result[i] = arr[:2] + } else { + result[i] = arr + } + } + return result +} + func formatErrorPath(path []string) string { return strings.Join(path, ".") } diff --git a/apps/dashboard/pkg/apis/dashboard/v2beta1/validation_test.go b/apps/dashboard/pkg/apis/dashboard/v2beta1/validation_test.go new file mode 100644 index 00000000000..226f329a761 --- /dev/null +++ b/apps/dashboard/pkg/apis/dashboard/v2beta1/validation_test.go @@ -0,0 +1,495 @@ +package v2beta1 + +import ( + "testing" + + "github.com/stretchr/testify/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestTrimStringArrays(t *testing.T) { + tests := []struct { + name string + input [][]string + expected [][]string + }{ + { + name: "nil input", + input: nil, + expected: nil, + }, + { + name: "empty input", + input: [][]string{}, + expected: [][]string{}, + }, + { + name: "arrays with exactly 2 elements", + input: [][]string{{"key1", "value1"}, {"key2", "value2"}}, + expected: [][]string{{"key1", "value1"}, {"key2", "value2"}}, + }, + { + name: "arrays with less than 2 elements", + input: [][]string{{"key1"}, {}}, + expected: [][]string{{"key1"}, {}}, + }, + { + name: "arrays with more than 2 elements", + input: [][]string{{"key1", "value1", "extra1"}, {"key2", "value2", "extra2", "extra3"}}, + expected: [][]string{{"key1", "value1"}, {"key2", "value2"}}, + }, + { + name: "mixed arrays", + input: [][]string{{"key1"}, {"key2", "value2"}, {"key3", "value3", "extra"}}, + expected: [][]string{{"key1"}, {"key2", "value2"}, {"key3", "value3"}}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := trimStringArrays(tt.input) + assert.Equal(t, tt.expected, result) + }) + } +} + +func TestProcessActions(t *testing.T) { + tests := []struct { + name string + actions []DashboardAction + expected []DashboardAction + }{ + { + name: "empty actions", + actions: []DashboardAction{}, + expected: []DashboardAction{}, + }, + { + name: "action with fetch options having oversized arrays", + actions: []DashboardAction{ + { + Type: DashboardActionTypeFetch, + Title: "Test Fetch", + Fetch: &DashboardFetchOptions{ + Method: DashboardHttpRequestMethodGET, + Url: "http://example.com", + QueryParams: [][]string{ + {"param1", "value1", "extra1"}, + {"param2", "value2"}, + }, + Headers: [][]string{ + {"header1", "value1", "extra1", "extra2"}, + {"header2", "value2"}, + }, + }, + }, + }, + expected: []DashboardAction{ + { + Type: DashboardActionTypeFetch, + Title: "Test Fetch", + Fetch: &DashboardFetchOptions{ + Method: DashboardHttpRequestMethodGET, + Url: "http://example.com", + QueryParams: [][]string{ + {"param1", "value1"}, + {"param2", "value2"}, + }, + Headers: [][]string{ + {"header1", "value1"}, + {"header2", "value2"}, + }, + }, + }, + }, + }, + { + name: "action with infinity options having oversized arrays", + actions: []DashboardAction{ + { + Type: DashboardActionTypeInfinity, + Title: "Test Infinity", + Infinity: &DashboardInfinityOptions{ + Method: DashboardHttpRequestMethodPOST, + Url: "http://example.com", + DatasourceUid: "test-uid", + QueryParams: [][]string{ + {"param1", "value1", "extra1"}, + {"param2", "value2"}, + }, + Headers: [][]string{ + {"header1", "value1", "extra1", "extra2"}, + {"header2", "value2"}, + }, + }, + }, + }, + expected: []DashboardAction{ + { + Type: DashboardActionTypeInfinity, + Title: "Test Infinity", + Infinity: &DashboardInfinityOptions{ + Method: DashboardHttpRequestMethodPOST, + Url: "http://example.com", + DatasourceUid: "test-uid", + QueryParams: [][]string{ + {"param1", "value1"}, + {"param2", "value2"}, + }, + Headers: [][]string{ + {"header1", "value1"}, + {"header2", "value2"}, + }, + }, + }, + }, + }, + { + name: "action without fetch or infinity options", + actions: []DashboardAction{ + { + Type: DashboardActionTypeFetch, + Title: "Test Action", + }, + }, + expected: []DashboardAction{ + { + Type: DashboardActionTypeFetch, + Title: "Test Action", + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Create a copy to avoid modifying the original + actionsCopy := make([]DashboardAction, len(tt.actions)) + for i, action := range tt.actions { + actionsCopy[i] = action + // Deep copy the fetch options if they exist + if action.Fetch != nil { + fetchCopy := *action.Fetch + if action.Fetch.QueryParams != nil { + fetchCopy.QueryParams = make([][]string, len(action.Fetch.QueryParams)) + for j, param := range action.Fetch.QueryParams { + fetchCopy.QueryParams[j] = make([]string, len(param)) + copy(fetchCopy.QueryParams[j], param) + } + } + if action.Fetch.Headers != nil { + fetchCopy.Headers = make([][]string, len(action.Fetch.Headers)) + for j, header := range action.Fetch.Headers { + fetchCopy.Headers[j] = make([]string, len(header)) + copy(fetchCopy.Headers[j], header) + } + } + actionsCopy[i].Fetch = &fetchCopy + } + // Deep copy the infinity options if they exist + if action.Infinity != nil { + infinityCopy := *action.Infinity + if action.Infinity.QueryParams != nil { + infinityCopy.QueryParams = make([][]string, len(action.Infinity.QueryParams)) + for j, param := range action.Infinity.QueryParams { + infinityCopy.QueryParams[j] = make([]string, len(param)) + copy(infinityCopy.QueryParams[j], param) + } + } + if action.Infinity.Headers != nil { + infinityCopy.Headers = make([][]string, len(action.Infinity.Headers)) + for j, header := range action.Infinity.Headers { + infinityCopy.Headers[j] = make([]string, len(header)) + copy(infinityCopy.Headers[j], header) + } + } + actionsCopy[i].Infinity = &infinityCopy + } + } + + processActions(actionsCopy) + assert.Equal(t, tt.expected, actionsCopy) + }) + } +} + +func TestValidateAndTrimActionArrays(t *testing.T) { + tests := []struct { + name string + dashboard *Dashboard + expected *Dashboard + }{ + { + name: "dashboard with no elements", + dashboard: &Dashboard{ + TypeMeta: metav1.TypeMeta{ + Kind: "Dashboard", + APIVersion: "v2beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-dashboard", + }, + Spec: DashboardSpec{ + Elements: map[string]DashboardElement{}, + }, + }, + expected: &Dashboard{ + TypeMeta: metav1.TypeMeta{ + Kind: "Dashboard", + APIVersion: "v2beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-dashboard", + }, + Spec: DashboardSpec{ + Elements: map[string]DashboardElement{}, + }, + }, + }, + { + name: "dashboard with panel having actions with oversized arrays", + dashboard: &Dashboard{ + TypeMeta: metav1.TypeMeta{ + Kind: "Dashboard", + APIVersion: "v2beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-dashboard", + }, + Spec: DashboardSpec{ + Elements: map[string]DashboardElement{ + "panel1": { + PanelKind: &DashboardPanelKind{ + Spec: DashboardPanelSpec{ + VizConfig: DashboardVizConfigKind{ + Spec: DashboardVizConfigSpec{ + FieldConfig: DashboardFieldConfigSource{ + Defaults: DashboardFieldConfig{ + Actions: []DashboardAction{ + { + Type: DashboardActionTypeFetch, + Title: "Test Action", + Fetch: &DashboardFetchOptions{ + Method: DashboardHttpRequestMethodGET, + Url: "http://example.com", + QueryParams: [][]string{ + {"param1", "value1", "extra1"}, + {"param2", "value2"}, + }, + Headers: [][]string{ + {"header1", "value1", "extra1", "extra2"}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + expected: &Dashboard{ + TypeMeta: metav1.TypeMeta{ + Kind: "Dashboard", + APIVersion: "v2beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-dashboard", + }, + Spec: DashboardSpec{ + Elements: map[string]DashboardElement{ + "panel1": { + PanelKind: &DashboardPanelKind{ + Spec: DashboardPanelSpec{ + VizConfig: DashboardVizConfigKind{ + Spec: DashboardVizConfigSpec{ + FieldConfig: DashboardFieldConfigSource{ + Defaults: DashboardFieldConfig{ + Actions: []DashboardAction{ + { + Type: DashboardActionTypeFetch, + Title: "Test Action", + Fetch: &DashboardFetchOptions{ + Method: DashboardHttpRequestMethodGET, + Url: "http://example.com", + QueryParams: [][]string{ + {"param1", "value1"}, + {"param2", "value2"}, + }, + Headers: [][]string{ + {"header1", "value1"}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "dashboard with panel having no actions", + dashboard: &Dashboard{ + TypeMeta: metav1.TypeMeta{ + Kind: "Dashboard", + APIVersion: "v2beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-dashboard", + }, + Spec: DashboardSpec{ + Elements: map[string]DashboardElement{ + "panel1": { + PanelKind: &DashboardPanelKind{ + Spec: DashboardPanelSpec{ + VizConfig: DashboardVizConfigKind{ + Spec: DashboardVizConfigSpec{ + FieldConfig: DashboardFieldConfigSource{ + Defaults: DashboardFieldConfig{ + Actions: nil, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + expected: &Dashboard{ + TypeMeta: metav1.TypeMeta{ + Kind: "Dashboard", + APIVersion: "v2beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-dashboard", + }, + Spec: DashboardSpec{ + Elements: map[string]DashboardElement{ + "panel1": { + PanelKind: &DashboardPanelKind{ + Spec: DashboardPanelSpec{ + VizConfig: DashboardVizConfigKind{ + Spec: DashboardVizConfigSpec{ + FieldConfig: DashboardFieldConfigSource{ + Defaults: DashboardFieldConfig{ + Actions: nil, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Create a deep copy to avoid modifying the original + dashboardCopy := deepCopyDashboard(tt.dashboard) + + validateAndTrimActionArrays(dashboardCopy) + assert.Equal(t, tt.expected, dashboardCopy) + }) + } +} + +// Helper function to create a deep copy of a Dashboard for testing +func deepCopyDashboard(original *Dashboard) *Dashboard { + if original == nil { + return nil + } + + result := &Dashboard{ + TypeMeta: original.TypeMeta, + ObjectMeta: original.ObjectMeta, + Spec: DashboardSpec{ + Elements: make(map[string]DashboardElement), + }, + Status: original.Status, + } + + for key, element := range original.Spec.Elements { + elementCopy := element + + if element.PanelKind != nil { + panelCopy := *element.PanelKind + elementCopy.PanelKind = &panelCopy + + if element.PanelKind.Spec.VizConfig.Spec.FieldConfig.Defaults.Actions != nil { + actions := element.PanelKind.Spec.VizConfig.Spec.FieldConfig.Defaults.Actions + actionsCopy := make([]DashboardAction, len(actions)) + + for j, action := range actions { + actionsCopy[j] = action + + // Deep copy fetch options + if action.Fetch != nil { + fetchCopy := *action.Fetch + if action.Fetch.QueryParams != nil { + fetchCopy.QueryParams = make([][]string, len(action.Fetch.QueryParams)) + for k, param := range action.Fetch.QueryParams { + fetchCopy.QueryParams[k] = make([]string, len(param)) + copy(fetchCopy.QueryParams[k], param) + } + } + if action.Fetch.Headers != nil { + fetchCopy.Headers = make([][]string, len(action.Fetch.Headers)) + for k, header := range action.Fetch.Headers { + fetchCopy.Headers[k] = make([]string, len(header)) + copy(fetchCopy.Headers[k], header) + } + } + actionsCopy[j].Fetch = &fetchCopy + } + + // Deep copy infinity options + if action.Infinity != nil { + infinityCopy := *action.Infinity + if action.Infinity.QueryParams != nil { + infinityCopy.QueryParams = make([][]string, len(action.Infinity.QueryParams)) + for k, param := range action.Infinity.QueryParams { + infinityCopy.QueryParams[k] = make([]string, len(param)) + copy(infinityCopy.QueryParams[k], param) + } + } + if action.Infinity.Headers != nil { + infinityCopy.Headers = make([][]string, len(action.Infinity.Headers)) + for k, header := range action.Infinity.Headers { + infinityCopy.Headers[k] = make([]string, len(header)) + copy(infinityCopy.Headers[k], header) + } + } + actionsCopy[j].Infinity = &infinityCopy + } + } + + elementCopy.PanelKind.Spec.VizConfig.Spec.FieldConfig.Defaults.Actions = actionsCopy + } + } + + if element.LibraryPanelKind != nil { + libraryCopy := *element.LibraryPanelKind + elementCopy.LibraryPanelKind = &libraryCopy + } + + result.Spec.Elements[key] = elementCopy + } + + return result +} diff --git a/apps/dashboard/pkg/apis/dashboard/v2beta1/zz_generated.openapi.go b/apps/dashboard/pkg/apis/dashboard/v2beta1/zz_generated.openapi.go index 8e3e22c83be..064f5f395b0 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2beta1/zz_generated.openapi.go +++ b/apps/dashboard/pkg/apis/dashboard/v2beta1/zz_generated.openapi.go @@ -18,6 +18,8 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.AnnotationPermission": schema_pkg_apis_dashboard_v2beta1_AnnotationPermission(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.Dashboard": schema_pkg_apis_dashboard_v2beta1_Dashboard(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAccess": schema_pkg_apis_dashboard_v2beta1_DashboardAccess(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAction": schema_pkg_apis_dashboard_v2beta1_DashboardAction(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardActionVariable": schema_pkg_apis_dashboard_v2beta1_DashboardActionVariable(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAdHocFilterWithLabels": schema_pkg_apis_dashboard_v2beta1_DashboardAdHocFilterWithLabels(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAdhocVariableKind": schema_pkg_apis_dashboard_v2beta1_DashboardAdhocVariableKind(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAdhocVariableSpec": schema_pkg_apis_dashboard_v2beta1_DashboardAdhocVariableSpec(ref), @@ -51,6 +53,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardDatasourceVariableSpec": schema_pkg_apis_dashboard_v2beta1_DashboardDatasourceVariableSpec(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardDynamicConfigValue": schema_pkg_apis_dashboard_v2beta1_DashboardDynamicConfigValue(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardElementReference": schema_pkg_apis_dashboard_v2beta1_DashboardElementReference(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardFetchOptions": schema_pkg_apis_dashboard_v2beta1_DashboardFetchOptions(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardFieldColor": schema_pkg_apis_dashboard_v2beta1_DashboardFieldColor(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardFieldConfig": schema_pkg_apis_dashboard_v2beta1_DashboardFieldConfig(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardFieldConfigSource": schema_pkg_apis_dashboard_v2beta1_DashboardFieldConfigSource(ref), @@ -62,6 +65,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardGridLayoutSpec": schema_pkg_apis_dashboard_v2beta1_DashboardGridLayoutSpec(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardGroupByVariableKind": schema_pkg_apis_dashboard_v2beta1_DashboardGroupByVariableKind(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardGroupByVariableSpec": schema_pkg_apis_dashboard_v2beta1_DashboardGroupByVariableSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardInfinityOptions": schema_pkg_apis_dashboard_v2beta1_DashboardInfinityOptions(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardIntervalVariableKind": schema_pkg_apis_dashboard_v2beta1_DashboardIntervalVariableKind(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardIntervalVariableSpec": schema_pkg_apis_dashboard_v2beta1_DashboardIntervalVariableSpec(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardJSONCodec": schema_pkg_apis_dashboard_v2beta1_DashboardJSONCodec(ref), @@ -108,6 +112,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardTimeRangeOption": schema_pkg_apis_dashboard_v2beta1_DashboardTimeRangeOption(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardTimeSettingsSpec": schema_pkg_apis_dashboard_v2beta1_DashboardTimeSettingsSpec(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardTransformationKind": schema_pkg_apis_dashboard_v2beta1_DashboardTransformationKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardV2beta1ActionStyle": schema_pkg_apis_dashboard_v2beta1_DashboardV2beta1ActionStyle(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardV2beta1AdhocVariableKindDatasource": schema_pkg_apis_dashboard_v2beta1_DashboardV2beta1AdhocVariableKindDatasource(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardV2beta1DataQueryKindDatasource": schema_pkg_apis_dashboard_v2beta1_DashboardV2beta1DataQueryKindDatasource(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardV2beta1FieldConfigSourceOverrides": schema_pkg_apis_dashboard_v2beta1_DashboardV2beta1FieldConfigSourceOverrides(ref), @@ -311,6 +316,109 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardAccess(ref common.ReferenceCallb } } +func schema_pkg_apis_dashboard_v2beta1_DashboardAction(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "title": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "fetch": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardFetchOptions"), + }, + }, + "infinity": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardInfinityOptions"), + }, + }, + "confirmation": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "oneClick": { + SchemaProps: spec.SchemaProps{ + Type: []string{"boolean"}, + Format: "", + }, + }, + "variables": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardActionVariable"), + }, + }, + }, + }, + }, + "style": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardV2beta1ActionStyle"), + }, + }, + }, + Required: []string{"type", "title"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardActionVariable", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardFetchOptions", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardInfinityOptions", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardV2beta1ActionStyle"}, + } +} + +func schema_pkg_apis_dashboard_v2beta1_DashboardActionVariable(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "key": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "type": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"key", "name", "type"}, + }, + }, + } +} + func schema_pkg_apis_dashboard_v2beta1_DashboardAdHocFilterWithLabels(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -1836,6 +1944,82 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardElementReference(ref common.Refe } } +func schema_pkg_apis_dashboard_v2beta1_DashboardFetchOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "method": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "url": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "body": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "queryParams": { + SchemaProps: spec.SchemaProps{ + Description: "These are 2D arrays of strings, each representing a key-value pair We are defining them this way because we can't generate a go struct that that would have exactly two strings in each sub-array", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + "headers": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + }, + Required: []string{"method", "url"}, + }, + }, + } +} + func schema_pkg_apis_dashboard_v2beta1_DashboardFieldColor(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -1988,6 +2172,20 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardFieldConfig(ref common.Reference }, }, }, + "actions": { + SchemaProps: spec.SchemaProps{ + Description: "Define interactive HTTP requests that can be triggered from data visualizations.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAction"), + }, + }, + }, + }, + }, "noValue": { SchemaProps: spec.SchemaProps{ Description: "Alternative to empty string", @@ -2014,7 +2212,7 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardFieldConfig(ref common.Reference }, }, Dependencies: []string{ - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardFieldColor", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardThresholdsConfig", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap"}, + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAction", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardFieldColor", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardThresholdsConfig", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap"}, } } @@ -2389,6 +2587,89 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardGroupByVariableSpec(ref common.R } } +func schema_pkg_apis_dashboard_v2beta1_DashboardInfinityOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "method": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "url": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "body": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "queryParams": { + SchemaProps: spec.SchemaProps{ + Description: "These are 2D arrays of strings, each representing a key-value pair We are defining them this way because we can't generate a go struct that that would have exactly two strings in each sub-array", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + "datasourceUid": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "headers": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + }, + Required: []string{"method", "url", "datasourceUid"}, + }, + }, + } +} + func schema_pkg_apis_dashboard_v2beta1_DashboardIntervalVariableKind(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -4389,6 +4670,24 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardTransformationKind(ref common.Re } } +func schema_pkg_apis_dashboard_v2beta1_DashboardV2beta1ActionStyle(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "backgroundColor": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + func schema_pkg_apis_dashboard_v2beta1_DashboardV2beta1AdhocVariableKindDatasource(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/apps/dashboard/pkg/apis/dashboard/v2beta1/zz_generated.openapi_violation_exceptions.list b/apps/dashboard/pkg/apis/dashboard/v2beta1/zz_generated.openapi_violation_exceptions.list index d516d5c8748..ef7c8aa4a3a 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2beta1/zz_generated.openapi_violation_exceptions.list +++ b/apps/dashboard/pkg/apis/dashboard/v2beta1/zz_generated.openapi_violation_exceptions.list @@ -1,3 +1,4 @@ +API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardAction,Variables API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardAdHocFilterWithLabels,ValueLabels API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardAdHocFilterWithLabels,Values API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardAdhocVariableSpec,BaseFilters @@ -9,11 +10,16 @@ API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/ API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardCustomVariableSpec,Options API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardDashboardLink,Tags API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardDatasourceVariableSpec,Options +API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardFetchOptions,Headers +API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardFetchOptions,QueryParams +API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardFieldConfig,Actions API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardFieldConfig,Links API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardFieldConfig,Mappings API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardFieldConfigSource,Overrides API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardGridLayoutSpec,Items API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardGroupByVariableSpec,Options +API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardInfinityOptions,Headers +API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardInfinityOptions,QueryParams API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardIntervalVariableSpec,Options API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardMetadata,Finalizers API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardPanelSpec,Links diff --git a/kinds/dashboard/dashboard_kind.cue b/kinds/dashboard/dashboard_kind.cue index ab4da8db7d9..20571c8211a 100644 --- a/kinds/dashboard/dashboard_kind.cue +++ b/kinds/dashboard/dashboard_kind.cue @@ -298,6 +298,59 @@ lineage: schemas: [{ // - "inControlsMenu" renders the link in bottom part of the dashboard controls dropdown menu #DashboardLinkPlacement: "inControlsMenu" @cuetsy(kind="type") + // Dashboard action type + #ActionType: "fetch" | "infinity" @cuetsy(kind="type") + + // Fetch options + #FetchOptions: { + method: #HttpRequestMethod + url: string + body?: string + // These are 2D arrays of strings, each representing a key-value pair + // We are defining this way because we can't generate a go struct that + // that would have exactly two strings in each sub-array + queryParams?: [...[...string]] + headers?: [...[...string]] + } @cuetsy(kind="interface") + + // Infinity options + #InfinityOptions: { + method: #HttpRequestMethod + url: string + body?: string + // These are 2D arrays of strings, each representing a key-value pair + // We are defining them this way because we can't generate a go struct that + // that would have exactly two strings in each sub-array + queryParams?: [...[...string]] + headers?: [...[...string]] + datasourceUid: string + } @cuetsy(kind="interface") + + #HttpRequestMethod: "GET" | "PUT" | "POST" | "DELETE" | "PATCH" @cuetsy(kind="type") + + // Action variable type + #ActionVariableType: "string" @cuetsy(kind="type") + + #ActionVariable: { + key: string + name: string + type: #ActionVariableType + } @cuetsy(kind="interface") + + // Dashboard action + #Action: { + type: #ActionType + title: string + fetch?: #FetchOptions + infinity?: #InfinityOptions + confirmation?: string + oneClick?: bool + variables?: [...#ActionVariable] + style?: { + backgroundColor?: string + } + } @cuetsy(kind="interface") + // 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). @@ -727,6 +780,9 @@ lineage: schemas: [{ // The behavior when clicking on a result links?: [...] @grafanamaturity(NeedsExpertReview) + // Define interactive HTTP requests that can be triggered from data visualizations. + actions?: [...#Action] @grafanamaturity(NeedsExpertReview) + // Alternative to empty string noValue?: string @grafanamaturity(NeedsExpertReview) diff --git a/packages/grafana-schema/src/index.gen.ts b/packages/grafana-schema/src/index.gen.ts index b608be01349..5646058691b 100644 --- a/packages/grafana-schema/src/index.gen.ts +++ b/packages/grafana-schema/src/index.gen.ts @@ -15,6 +15,13 @@ export type { DashboardLink, DashboardLinkType, DashboardLinkPlacement, + ActionType, + FetchOptions, + InfinityOptions, + HttpRequestMethod, + ActionVariableType, + ActionVariable, + Action, VariableType, FieldColorSeriesByMode, FieldColor, @@ -37,6 +44,9 @@ export { VariableRefresh, VariableSort, defaultDashboardLink, + defaultFetchOptions, + defaultInfinityOptions, + defaultAction, FieldColorModeId, defaultGridPos, ThresholdsMode, diff --git a/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts b/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts index baf09ea16ce..d158d884aa0 100644 --- a/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts +++ b/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts @@ -363,6 +363,87 @@ export type DashboardLinkType = ('link' | 'dashboards'); */ export type DashboardLinkPlacement = 'inControlsMenu'; +/** + * Dashboard action type + */ +export type ActionType = ('fetch' | 'infinity'); + +/** + * Fetch options + */ +export interface FetchOptions { + body?: string; + headers?: Array>; + method: HttpRequestMethod; + /** + * These are 2D arrays of strings, each representing a key-value pair + * We are defining this way because we can't generate a go struct that + * that would have exactly two strings in each sub-array + */ + queryParams?: Array>; + url: string; +} + +export const defaultFetchOptions: Partial = { + headers: [], + queryParams: [], +}; + +/** + * Infinity options + */ +export interface InfinityOptions { + body?: string; + datasourceUid: string; + headers?: Array>; + method: HttpRequestMethod; + /** + * These are 2D arrays of strings, each representing a key-value pair + * We are defining them this way because we can't generate a go struct that + * that would have exactly two strings in each sub-array + */ + queryParams?: Array>; + url: string; +} + +export const defaultInfinityOptions: Partial = { + headers: [], + queryParams: [], +}; + +export type HttpRequestMethod = ('GET' | 'PUT' | 'POST' | 'DELETE' | 'PATCH'); + +/** + * Action variable type + */ +export type ActionVariableType = 'string'; + +export interface ActionVariable { + key: string; + name: string; + type: ActionVariableType; +} + +/** + * Dashboard action + */ +export interface Action { + confirmation?: string; + fetch?: FetchOptions; + infinity?: InfinityOptions; + oneClick?: boolean; + style?: { + backgroundColor?: string; + }; + title: string; + type: ActionType; + variables?: Array; +} + +export const defaultAction: Partial = { + variables: [], +}; + /** * Dashboard variable type * `query`: Query-generated list of values such as metric names, server names, sensor IDs, data centers, and so on. @@ -916,6 +997,10 @@ export const defaultMatcherConfig: Partial = { * Field options allow you to change how the data is displayed in your visualizations. */ export interface FieldConfig { + /** + * Define interactive HTTP requests that can be triggered from data visualizations. + */ + actions?: Array; /** * Panel color configuration */ @@ -1001,6 +1086,7 @@ export interface FieldConfig { } export const defaultFieldConfig: Partial = { + actions: [], links: [], mappings: [], }; 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 index d821bff4532..8c6adae3078 100644 --- a/packages/grafana-schema/src/schema/dashboard/v2alpha1/types.spec.gen.ts +++ b/packages/grafana-schema/src/schema/dashboard/v2alpha1/types.spec.gen.ts @@ -318,6 +318,8 @@ export interface FieldConfig { color?: FieldColor; // The behavior when clicking on a result links?: any[]; + // Define interactive HTTP requests that can be triggered from data visualizations. + actions?: Action[]; // Alternative to empty string noValue?: string; // custom is specified by the FieldConfig field @@ -505,6 +507,81 @@ export type FieldColorSeriesByMode = "min" | "max" | "last"; export const defaultFieldColorSeriesByMode = (): FieldColorSeriesByMode => ("min"); +export interface Action { + type: ActionType; + title: string; + fetch?: FetchOptions; + infinity?: InfinityOptions; + confirmation?: string; + oneClick?: boolean; + variables?: ActionVariable[]; + style?: { + backgroundColor?: string; + }; +} + +export const defaultAction = (): Action => ({ + type: "fetch", + title: "", +}); + +export type ActionType = "fetch" | "infinity"; + +export const defaultActionType = (): ActionType => ("fetch"); + +export interface FetchOptions { + method: HttpRequestMethod; + url: string; + body?: string; + // These are 2D arrays of strings, each representing a key-value pair + // We are defining them this way because we can't generate a go struct that + // that would have exactly two strings in each sub-array + queryParams?: string[][]; + headers?: string[][]; +} + +export const defaultFetchOptions = (): FetchOptions => ({ + method: "GET", + url: "", +}); + +export type HttpRequestMethod = "GET" | "PUT" | "POST" | "DELETE" | "PATCH"; + +export const defaultHttpRequestMethod = (): HttpRequestMethod => ("GET"); + +export interface InfinityOptions { + method: HttpRequestMethod; + url: string; + body?: string; + // These are 2D arrays of strings, each representing a key-value pair + // We are defining them this way because we can't generate a go struct that + // that would have exactly two strings in each sub-array + queryParams?: string[][]; + datasourceUid: string; + headers?: string[][]; +} + +export const defaultInfinityOptions = (): InfinityOptions => ({ + method: "GET", + url: "", + datasourceUid: "", +}); + +export interface ActionVariable { + key: string; + name: string; + type: "string"; +} + +export const defaultActionVariable = (): ActionVariable => ({ + key: "", + name: "", + type: ActionVariableType, +}); + +// Action variable type +export const ActionVariableType = "string"; + export interface DynamicConfigValue { id: string; value?: any; diff --git a/packages/grafana-schema/src/schema/dashboard/v2beta1/types.spec.gen.ts b/packages/grafana-schema/src/schema/dashboard/v2beta1/types.spec.gen.ts index 65739dc88ec..e095f7b948b 100644 --- a/packages/grafana-schema/src/schema/dashboard/v2beta1/types.spec.gen.ts +++ b/packages/grafana-schema/src/schema/dashboard/v2beta1/types.spec.gen.ts @@ -317,6 +317,8 @@ export interface FieldConfig { color?: FieldColor; // The behavior when clicking on a result links?: any[]; + // Define interactive HTTP requests that can be triggered from data visualizations. + actions?: Action[]; // Alternative to empty string noValue?: string; // custom is specified by the FieldConfig field @@ -504,6 +506,81 @@ export type FieldColorSeriesByMode = "min" | "max" | "last"; export const defaultFieldColorSeriesByMode = (): FieldColorSeriesByMode => ("min"); +export interface Action { + type: ActionType; + title: string; + fetch?: FetchOptions; + infinity?: InfinityOptions; + confirmation?: string; + oneClick?: boolean; + variables?: ActionVariable[]; + style?: { + backgroundColor?: string; + }; +} + +export const defaultAction = (): Action => ({ + type: "fetch", + title: "", +}); + +export type ActionType = "fetch" | "infinity"; + +export const defaultActionType = (): ActionType => ("fetch"); + +export interface FetchOptions { + method: HttpRequestMethod; + url: string; + body?: string; + // These are 2D arrays of strings, each representing a key-value pair + // We are defining them this way because we can't generate a go struct that + // that would have exactly two strings in each sub-array + queryParams?: string[][]; + headers?: string[][]; +} + +export const defaultFetchOptions = (): FetchOptions => ({ + method: "GET", + url: "", +}); + +export type HttpRequestMethod = "GET" | "PUT" | "POST" | "DELETE" | "PATCH"; + +export const defaultHttpRequestMethod = (): HttpRequestMethod => ("GET"); + +export interface InfinityOptions { + method: HttpRequestMethod; + url: string; + body?: string; + // These are 2D arrays of strings, each representing a key-value pair + // We are defining them this way because we can't generate a go struct that + // that would have exactly two strings in each sub-array + queryParams?: string[][]; + datasourceUid: string; + headers?: string[][]; +} + +export const defaultInfinityOptions = (): InfinityOptions => ({ + method: "GET", + url: "", + datasourceUid: "", +}); + +export interface ActionVariable { + key: string; + name: string; + type: "string"; +} + +export const defaultActionVariable = (): ActionVariable => ({ + key: "", + name: "", + type: ActionVariableType, +}); + +// Action variable type +export const ActionVariableType = "string"; + export interface DynamicConfigValue { id: string; value?: any; diff --git a/pkg/kinds/dashboard/dashboard_spec_gen.go b/pkg/kinds/dashboard/dashboard_spec_gen.go index e01c43d2229..fa72f781f75 100644 --- a/pkg/kinds/dashboard/dashboard_spec_gen.go +++ b/pkg/kinds/dashboard/dashboard_spec_gen.go @@ -427,6 +427,8 @@ type FieldConfig struct { Color *FieldColor `json:"color,omitempty"` // The behavior when clicking on a result Links []any `json:"links,omitempty"` + // Define interactive HTTP requests that can be triggered from data visualizations. + Actions []Action `json:"actions,omitempty"` // Alternative to empty string NoValue *string `json:"noValue,omitempty"` // custom is specified by the FieldConfig field @@ -654,6 +656,92 @@ const ( FieldColorSeriesByModeLast FieldColorSeriesByMode = "last" ) +// Dashboard action +type Action struct { + Type ActionType `json:"type"` + Title string `json:"title"` + Fetch *FetchOptions `json:"fetch,omitempty"` + Infinity *InfinityOptions `json:"infinity,omitempty"` + Confirmation *string `json:"confirmation,omitempty"` + OneClick *bool `json:"oneClick,omitempty"` + Variables []ActionVariable `json:"variables,omitempty"` + Style *DashboardActionStyle `json:"style,omitempty"` +} + +// NewAction creates a new Action object. +func NewAction() *Action { + return &Action{} +} + +// Dashboard action type +type ActionType string + +const ( + ActionTypeFetch ActionType = "fetch" + ActionTypeInfinity ActionType = "infinity" +) + +// Fetch options +type FetchOptions struct { + Method HttpRequestMethod `json:"method"` + Url string `json:"url"` + Body *string `json:"body,omitempty"` + // These are 2D arrays of strings, each representing a key-value pair + // We are defining this way because we can't generate a go struct that + // that would have exactly two strings in each sub-array + QueryParams [][]string `json:"queryParams,omitempty"` + Headers [][]string `json:"headers,omitempty"` +} + +// NewFetchOptions creates a new FetchOptions object. +func NewFetchOptions() *FetchOptions { + return &FetchOptions{} +} + +type HttpRequestMethod string + +const ( + HttpRequestMethodGET HttpRequestMethod = "GET" + HttpRequestMethodPUT HttpRequestMethod = "PUT" + HttpRequestMethodPOST HttpRequestMethod = "POST" + HttpRequestMethodDELETE HttpRequestMethod = "DELETE" + HttpRequestMethodPATCH HttpRequestMethod = "PATCH" +) + +// Infinity options +type InfinityOptions struct { + Method HttpRequestMethod `json:"method"` + Url string `json:"url"` + Body *string `json:"body,omitempty"` + // These are 2D arrays of strings, each representing a key-value pair + // We are defining them this way because we can't generate a go struct that + // that would have exactly two strings in each sub-array + QueryParams [][]string `json:"queryParams,omitempty"` + Headers [][]string `json:"headers,omitempty"` + DatasourceUid string `json:"datasourceUid"` +} + +// NewInfinityOptions creates a new InfinityOptions object. +func NewInfinityOptions() *InfinityOptions { + return &InfinityOptions{} +} + +type ActionVariable struct { + Key string `json:"key"` + Name string `json:"name"` + Type string `json:"type"` +} + +// NewActionVariable creates a new ActionVariable object. +func NewActionVariable() *ActionVariable { + return &ActionVariable{ + Type: ActionVariableType, + } +} + +// Action variable type +const ActionVariableType = "string" + type DynamicConfigValue struct { Id string `json:"id"` Value any `json:"value,omitempty"` @@ -1042,6 +1130,15 @@ func NewDashboardSpecialValueMapOptions() *DashboardSpecialValueMapOptions { } } +type DashboardActionStyle struct { + BackgroundColor *string `json:"backgroundColor,omitempty"` +} + +// NewDashboardActionStyle creates a new DashboardActionStyle object. +func NewDashboardActionStyle() *DashboardActionStyle { + return &DashboardActionStyle{} +} + type PanelRepeatDirection string const ( diff --git a/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v2alpha1.json b/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v2alpha1.json index aab8e377285..ddc33887775 100644 --- a/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v2alpha1.json +++ b/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v2alpha1.json @@ -1060,6 +1060,71 @@ } } }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAction": { + "type": "object", + "required": [ + "type", + "title" + ], + "properties": { + "confirmation": { + "type": "string" + }, + "fetch": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardFetchOptions" + }, + "infinity": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardInfinityOptions" + }, + "oneClick": { + "type": "boolean" + }, + "style": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardV2alpha1ActionStyle" + }, + "title": { + "type": "string", + "default": "" + }, + "type": { + "type": "string", + "default": "" + }, + "variables": { + "type": "array", + "items": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardActionVariable" + } + ] + } + } + } + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardActionVariable": { + "type": "object", + "required": [ + "key", + "name", + "type" + ], + "properties": { + "key": { + "type": "string", + "default": "" + }, + "name": { + "type": "string", + "default": "" + }, + "type": { + "type": "string", + "default": "" + } + } + }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAdHocFilterWithLabels": { "description": "Define the AdHocFilterWithLabels type", "type": "object", @@ -2062,6 +2127,47 @@ } } }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardFetchOptions": { + "type": "object", + "required": [ + "method", + "url" + ], + "properties": { + "body": { + "type": "string" + }, + "headers": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "string", + "default": "" + } + } + }, + "method": { + "type": "string", + "default": "" + }, + "queryParams": { + "description": "These are 2D arrays of strings, each representing a key-value pair We are defining them this way because we can't generate a go struct that that would have exactly two strings in each sub-array", + "type": "array", + "items": { + "type": "array", + "items": { + "type": "string", + "default": "" + } + } + }, + "url": { + "type": "string", + "default": "" + } + } + }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardFieldColor": { "description": "Map a field to a color.", "type": "object", @@ -2088,6 +2194,18 @@ "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": "object", "properties": { + "actions": { + "description": "Define interactive HTTP requests that can be triggered from data visualizations.", + "type": "array", + "items": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAction" + } + ] + } + }, "color": { "description": "Panel color configuration", "allOf": [ @@ -2427,6 +2545,52 @@ } } }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardInfinityOptions": { + "type": "object", + "required": [ + "method", + "url", + "datasourceUid" + ], + "properties": { + "body": { + "type": "string" + }, + "datasourceUid": { + "type": "string", + "default": "" + }, + "headers": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "string", + "default": "" + } + } + }, + "method": { + "type": "string", + "default": "" + }, + "queryParams": { + "description": "These are 2D arrays of strings, each representing a key-value pair We are defining them this way because we can't generate a go struct that that would have exactly two strings in each sub-array", + "type": "array", + "items": { + "type": "array", + "items": { + "type": "string", + "default": "" + } + } + }, + "url": { + "type": "string", + "default": "" + } + } + }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardIntervalVariableKind": { "description": "Interval variable kind", "type": "object", @@ -3735,6 +3899,14 @@ } } }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardV2alpha1ActionStyle": { + "type": "object", + "properties": { + "backgroundColor": { + "type": "string" + } + } + }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardV2alpha1FieldConfigSourceOverrides": { "type": "object", "required": [ diff --git a/public/app/features/dashboard-scene/serialization/transformToV2TypesUtils.test.ts b/public/app/features/dashboard-scene/serialization/transformToV2TypesUtils.test.ts index b1633283c97..548be1e0233 100644 --- a/public/app/features/dashboard-scene/serialization/transformToV2TypesUtils.test.ts +++ b/public/app/features/dashboard-scene/serialization/transformToV2TypesUtils.test.ts @@ -21,33 +21,33 @@ describe('transformToV2TypesUtils', () => { expect(transformCursorSynctoEnum(undefined)).toBe(defaultDashboardCursorSync()); }); }); -}); -describe('transformVariableRefreshToEnum', () => { - it('should return the correct enum value for variable refresh', () => { - expect(transformVariableRefreshToEnum(0)).toBe('never'); - expect(transformVariableRefreshToEnum(1)).toBe('onDashboardLoad'); - expect(transformVariableRefreshToEnum(2)).toBe('onTimeRangeChanged'); - expect(transformVariableRefreshToEnum(undefined)).toBe(defaultVariableRefresh()); + describe('transformVariableRefreshToEnum', () => { + it('should return the correct enum value for variable refresh', () => { + expect(transformVariableRefreshToEnum(0)).toBe('never'); + expect(transformVariableRefreshToEnum(1)).toBe('onDashboardLoad'); + expect(transformVariableRefreshToEnum(2)).toBe('onTimeRangeChanged'); + expect(transformVariableRefreshToEnum(undefined)).toBe(defaultVariableRefresh()); + }); }); -}); -describe('transformVariableHideToEnum', () => { - it('should return the correct enum value for variable hide', () => { - expect(transformVariableHideToEnum(0)).toBe('dontHide'); - expect(transformVariableHideToEnum(1)).toBe('hideLabel'); - expect(transformVariableHideToEnum(2)).toBe('hideVariable'); - expect(transformVariableHideToEnum(undefined)).toBe(defaultVariableHide()); + describe('transformVariableHideToEnum', () => { + it('should return the correct enum value for variable hide', () => { + expect(transformVariableHideToEnum(0)).toBe('dontHide'); + expect(transformVariableHideToEnum(1)).toBe('hideLabel'); + expect(transformVariableHideToEnum(2)).toBe('hideVariable'); + expect(transformVariableHideToEnum(undefined)).toBe(defaultVariableHide()); + }); }); -}); -describe('transformSortVariableToEnum', () => { - it('should return the correct enum value for variable sort', () => { - expect(transformSortVariableToEnum(0)).toBe('disabled'); - expect(transformSortVariableToEnum(1)).toBe('alphabeticalAsc'); - expect(transformSortVariableToEnum(2)).toBe('alphabeticalDesc'); - expect(transformSortVariableToEnum(3)).toBe('numericalAsc'); - expect(transformSortVariableToEnum(4)).toBe('numericalDesc'); - expect(transformSortVariableToEnum(undefined)).toBe(defaultVariableSort()); + describe('transformSortVariableToEnum', () => { + it('should return the correct enum value for variable sort', () => { + expect(transformSortVariableToEnum(0)).toBe('disabled'); + expect(transformSortVariableToEnum(1)).toBe('alphabeticalAsc'); + expect(transformSortVariableToEnum(2)).toBe('alphabeticalDesc'); + expect(transformSortVariableToEnum(3)).toBe('numericalAsc'); + expect(transformSortVariableToEnum(4)).toBe('numericalDesc'); + expect(transformSortVariableToEnum(undefined)).toBe(defaultVariableSort()); + }); }); }); From 6afd532635ed33c9ce33d07bac22cc2d209cc1e4 Mon Sep 17 00:00:00 2001 From: Yuri Tseretyan Date: Fri, 12 Sep 2025 13:32:06 -0400 Subject: [PATCH 03/33] Make alerting team a sole owner of alerting tests (#111036) make alerting team sole owner of alerting tests --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 905cfad2951..a052bdbac8c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -187,7 +187,7 @@ /pkg/setting/ @grafana/grafana-backend-services-squad /pkg/tests/ @grafana/grafana-backend-services-squad /pkg/tests/apis/ @grafana/grafana-app-platform-squad -/pkg/tests/apis/alerting @grafana/grafana-app-platform-squad @grafana/alerting-backend +/pkg/tests/apis/alerting @grafana/alerting-backend /pkg/tests/apis/features @grafana/grafana-backend-services-squad /pkg/tests/apis/folder @grafana/grafana-search-and-storage /pkg/tests/apis/iam @grafana/identity-access-team From afc536118d4b17567289393a39394f19df70d217 Mon Sep 17 00:00:00 2001 From: "grafana-delivery-bot[bot]" <132647405+grafana-delivery-bot[bot]@users.noreply.github.com> Date: Fri, 12 Sep 2025 17:54:11 +0000 Subject: [PATCH 04/33] Release: Bump version to 12.3.0-pre (#110974) * update bump-version * Add id-token: write * update generate-token step * pull-requests -> pull_requests * clone with token and set right name * bump version 12.3.0-pre --------- Co-authored-by: Kevin Minehart <5140827+kminehart@users.noreply.github.com> Co-authored-by: grafana-delivery-bot[bot] --- .../grafana-extensionstest-app/package.json | 2 +- .../grafana-test-datasource/package.json | 2 +- lerna.json | 2 +- package.json | 2 +- packages/grafana-alerting/package.json | 4 +- packages/grafana-data/package.json | 6 +- packages/grafana-e2e-selectors/package.json | 2 +- packages/grafana-eslint-rules/package.json | 2 +- packages/grafana-flamegraph/package.json | 6 +- packages/grafana-i18n/package.json | 2 +- .../grafana-o11y-ds-frontend/package.json | 12 +- packages/grafana-plugin-configs/package.json | 2 +- packages/grafana-prometheus/package.json | 14 +- packages/grafana-runtime/package.json | 10 +- packages/grafana-schema/package.json | 2 +- .../x/AnnotationsListPanelCfg_types.gen.ts | 2 +- .../panelcfg/x/BarChartPanelCfg_types.gen.ts | 2 +- .../panelcfg/x/BarGaugePanelCfg_types.gen.ts | 2 +- .../x/CandlestickPanelCfg_types.gen.ts | 2 +- .../panelcfg/x/CanvasPanelCfg_types.gen.ts | 2 +- .../x/CloudWatchDataQuery_types.gen.ts | 2 +- .../x/DashboardListPanelCfg_types.gen.ts | 2 +- .../panelcfg/x/DatagridPanelCfg_types.gen.ts | 2 +- .../panelcfg/x/DebugPanelCfg_types.gen.ts | 2 +- .../x/ElasticsearchDataQuery_types.gen.ts | 2 +- .../panelcfg/x/GaugePanelCfg_types.gen.ts | 2 +- .../panelcfg/x/GeomapPanelCfg_types.gen.ts | 2 +- .../panelcfg/x/HeatmapPanelCfg_types.gen.ts | 2 +- .../panelcfg/x/HistogramPanelCfg_types.gen.ts | 2 +- .../logs/panelcfg/x/LogsPanelCfg_types.gen.ts | 2 +- .../news/panelcfg/x/NewsPanelCfg_types.gen.ts | 2 +- .../panelcfg/x/NodeGraphPanelCfg_types.gen.ts | 2 +- .../panelcfg/x/PieChartPanelCfg_types.gen.ts | 2 +- .../stat/panelcfg/x/StatPanelCfg_types.gen.ts | 2 +- .../x/StateTimelinePanelCfg_types.gen.ts | 2 +- .../x/StatusHistoryPanelCfg_types.gen.ts | 2 +- .../panelcfg/x/TablePanelCfg_types.gen.ts | 2 +- .../text/panelcfg/x/TextPanelCfg_types.gen.ts | 2 +- .../x/TimeSeriesPanelCfg_types.gen.ts | 2 +- .../panelcfg/x/TrendPanelCfg_types.gen.ts | 2 +- .../panelcfg/x/XYChartPanelCfg_types.gen.ts | 2 +- packages/grafana-sql/package.json | 12 +- packages/grafana-test-utils/package.json | 2 +- packages/grafana-ui/package.json | 10 +- .../datasource/azuremonitor/package.json | 16 +- .../datasource/cloud-monitoring/package.json | 14 +- .../package.json | 14 +- .../grafana-pyroscope-datasource/package.json | 12 +- .../grafana-testdata-datasource/package.json | 14 +- .../plugins/datasource/graphite/package.json | 14 +- .../plugins/datasource/jaeger/package.json | 2 +- .../app/plugins/datasource/loki/package.json | 14 +- .../app/plugins/datasource/mssql/package.json | 16 +- .../app/plugins/datasource/mysql/package.json | 14 +- .../app/plugins/datasource/parca/package.json | 12 +- .../app/plugins/datasource/tempo/package.json | 4 +- .../plugins/datasource/zipkin/package.json | 2 +- yarn.lock | 196 +++++++++--------- 58 files changed, 245 insertions(+), 245 deletions(-) diff --git a/e2e-playwright/test-plugins/grafana-extensionstest-app/package.json b/e2e-playwright/test-plugins/grafana-extensionstest-app/package.json index 89d6c90be5d..e1960c9515e 100644 --- a/e2e-playwright/test-plugins/grafana-extensionstest-app/package.json +++ b/e2e-playwright/test-plugins/grafana-extensionstest-app/package.json @@ -1,6 +1,6 @@ { "name": "@test-plugins/extensions-test-app", - "version": "12.2.0-pre", + "version": "12.3.0-pre", "private": true, "scripts": { "build": "NODE_OPTIONS='--experimental-strip-types --no-warnings=ExperimentalWarning' webpack -c ./webpack.config.ts --env production", diff --git a/e2e-playwright/test-plugins/grafana-test-datasource/package.json b/e2e-playwright/test-plugins/grafana-test-datasource/package.json index 7ed88373c45..71b032b1d88 100644 --- a/e2e-playwright/test-plugins/grafana-test-datasource/package.json +++ b/e2e-playwright/test-plugins/grafana-test-datasource/package.json @@ -1,6 +1,6 @@ { "name": "@test-plugins/grafana-e2etest-datasource", - "version": "12.2.0-pre", + "version": "12.3.0-pre", "private": true, "scripts": { "build": "NODE_OPTIONS='--experimental-strip-types --no-warnings=ExperimentalWarning' webpack -c ./webpack.config.ts --env production", diff --git a/lerna.json b/lerna.json index fe2cdbf0402..70b729cb019 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { "$schema": "node_modules/lerna/schemas/lerna-schema.json", "npmClient": "yarn", - "version": "12.2.0-pre" + "version": "12.3.0-pre" } diff --git a/package.json b/package.json index 20bbe6043fa..747a6a6bce3 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "license": "AGPL-3.0-only", "private": true, "name": "grafana", - "version": "12.2.0-pre", + "version": "12.3.0-pre", "repository": "github:grafana/grafana", "scripts": { "predev": "./scripts/check-frontend-dev.sh", diff --git a/packages/grafana-alerting/package.json b/packages/grafana-alerting/package.json index cf497d215c7..f162791a6bf 100644 --- a/packages/grafana-alerting/package.json +++ b/packages/grafana-alerting/package.json @@ -2,7 +2,7 @@ "author": "Grafana Labs", "license": "Apache-2.0", "name": "@grafana/alerting", - "version": "12.2.0-pre", + "version": "12.3.0-pre", "description": "Grafana Alerting Library – Build vertical integrations on top of the industry-leading alerting solution", "keywords": [ "typescript", @@ -93,7 +93,7 @@ "dependencies": { "@emotion/css": "11.13.5", "@faker-js/faker": "^9.8.0", - "@grafana/i18n": "12.2.0-pre", + "@grafana/i18n": "12.3.0-pre", "fishery": "^2.3.1", "lodash": "^4.17.21" } diff --git a/packages/grafana-data/package.json b/packages/grafana-data/package.json index 9b0733b3441..bb6a57974c8 100644 --- a/packages/grafana-data/package.json +++ b/packages/grafana-data/package.json @@ -2,7 +2,7 @@ "author": "Grafana Labs", "license": "Apache-2.0", "name": "@grafana/data", - "version": "12.2.0-pre", + "version": "12.3.0-pre", "description": "Grafana Data Library", "keywords": [ "typescript" @@ -56,8 +56,8 @@ }, "dependencies": { "@braintree/sanitize-url": "7.0.1", - "@grafana/i18n": "12.2.0-pre", - "@grafana/schema": "12.2.0-pre", + "@grafana/i18n": "12.3.0-pre", + "@grafana/schema": "12.3.0-pre", "@leeoniya/ufuzzy": "1.0.18", "@types/d3-interpolate": "^3.0.0", "@types/string-hash": "1.1.3", diff --git a/packages/grafana-e2e-selectors/package.json b/packages/grafana-e2e-selectors/package.json index b5bf9b4b27c..1b6dde9a254 100644 --- a/packages/grafana-e2e-selectors/package.json +++ b/packages/grafana-e2e-selectors/package.json @@ -2,7 +2,7 @@ "author": "Grafana Labs", "license": "Apache-2.0", "name": "@grafana/e2e-selectors", - "version": "12.2.0-pre", + "version": "12.3.0-pre", "description": "Grafana End-to-End Test Selectors Library", "keywords": [ "cli", diff --git a/packages/grafana-eslint-rules/package.json b/packages/grafana-eslint-rules/package.json index 71203b6d524..f182cd0281b 100644 --- a/packages/grafana-eslint-rules/package.json +++ b/packages/grafana-eslint-rules/package.json @@ -1,7 +1,7 @@ { "name": "@grafana/eslint-plugin", "description": "ESLint rules for use within the Grafana repo. Not suitable (or supported) for external use.", - "version": "12.2.0-pre", + "version": "12.3.0-pre", "main": "./index.cjs", "author": "Grafana Labs", "license": "Apache-2.0", diff --git a/packages/grafana-flamegraph/package.json b/packages/grafana-flamegraph/package.json index 3313be6f7f0..5d9087e4516 100644 --- a/packages/grafana-flamegraph/package.json +++ b/packages/grafana-flamegraph/package.json @@ -2,7 +2,7 @@ "author": "Grafana Labs", "license": "Apache-2.0", "name": "@grafana/flamegraph", - "version": "12.2.0-pre", + "version": "12.3.0-pre", "description": "Grafana flamegraph visualization component", "keywords": [ "grafana", @@ -44,8 +44,8 @@ ], "dependencies": { "@emotion/css": "11.13.5", - "@grafana/data": "12.2.0-pre", - "@grafana/ui": "12.2.0-pre", + "@grafana/data": "12.3.0-pre", + "@grafana/ui": "12.3.0-pre", "@leeoniya/ufuzzy": "1.0.18", "d3": "^7.8.5", "lodash": "4.17.21", diff --git a/packages/grafana-i18n/package.json b/packages/grafana-i18n/package.json index 12680665aea..365211e2faa 100644 --- a/packages/grafana-i18n/package.json +++ b/packages/grafana-i18n/package.json @@ -2,7 +2,7 @@ "author": "Grafana Labs", "license": "Apache-2.0", "name": "@grafana/i18n", - "version": "12.2.0-pre", + "version": "12.3.0-pre", "description": "Grafana Internationalization Library", "keywords": [ "grafana", diff --git a/packages/grafana-o11y-ds-frontend/package.json b/packages/grafana-o11y-ds-frontend/package.json index b252f5c9246..5694659ab70 100644 --- a/packages/grafana-o11y-ds-frontend/package.json +++ b/packages/grafana-o11y-ds-frontend/package.json @@ -3,7 +3,7 @@ "license": "AGPL-3.0-only", "name": "@grafana/o11y-ds-frontend", "private": true, - "version": "12.2.0-pre", + "version": "12.3.0-pre", "description": "Library to manage traces in Grafana.", "sideEffects": false, "repository": { @@ -18,12 +18,12 @@ }, "dependencies": { "@emotion/css": "11.13.5", - "@grafana/data": "12.2.0-pre", - "@grafana/e2e-selectors": "12.2.0-pre", + "@grafana/data": "12.3.0-pre", + "@grafana/e2e-selectors": "12.3.0-pre", "@grafana/plugin-ui": "^0.10.10", - "@grafana/runtime": "12.2.0-pre", - "@grafana/schema": "12.2.0-pre", - "@grafana/ui": "12.2.0-pre", + "@grafana/runtime": "12.3.0-pre", + "@grafana/schema": "12.3.0-pre", + "@grafana/ui": "12.3.0-pre", "react-select": "5.10.2", "react-use": "17.6.0", "rxjs": "7.8.2", diff --git a/packages/grafana-plugin-configs/package.json b/packages/grafana-plugin-configs/package.json index aebe52115bc..d016c3b8504 100644 --- a/packages/grafana-plugin-configs/package.json +++ b/packages/grafana-plugin-configs/package.json @@ -2,7 +2,7 @@ "name": "@grafana/plugin-configs", "description": "Shared dependencies and files for core plugins", "private": true, - "version": "12.2.0-pre", + "version": "12.3.0-pre", "dependencies": { "tslib": "2.8.1" }, diff --git a/packages/grafana-prometheus/package.json b/packages/grafana-prometheus/package.json index 4fd2566ce4e..be492d4c455 100644 --- a/packages/grafana-prometheus/package.json +++ b/packages/grafana-prometheus/package.json @@ -2,7 +2,7 @@ "author": "Grafana Labs", "license": "AGPL-3.0-only", "name": "@grafana/prometheus", - "version": "12.2.0-pre", + "version": "12.3.0-pre", "description": "Grafana Prometheus Library", "keywords": [ "typescript", @@ -41,13 +41,13 @@ "dependencies": { "@emotion/css": "11.13.5", "@floating-ui/react": "0.27.16", - "@grafana/data": "12.2.0-pre", - "@grafana/e2e-selectors": "12.2.0-pre", - "@grafana/i18n": "12.2.0-pre", + "@grafana/data": "12.3.0-pre", + "@grafana/e2e-selectors": "12.3.0-pre", + "@grafana/i18n": "12.3.0-pre", "@grafana/plugin-ui": "^0.10.10", - "@grafana/runtime": "12.2.0-pre", - "@grafana/schema": "12.2.0-pre", - "@grafana/ui": "12.2.0-pre", + "@grafana/runtime": "12.3.0-pre", + "@grafana/schema": "12.3.0-pre", + "@grafana/ui": "12.3.0-pre", "@hello-pangea/dnd": "18.0.1", "@leeoniya/ufuzzy": "1.0.18", "@lezer/common": "1.2.3", diff --git a/packages/grafana-runtime/package.json b/packages/grafana-runtime/package.json index 368ce72381f..c4e32ce6340 100644 --- a/packages/grafana-runtime/package.json +++ b/packages/grafana-runtime/package.json @@ -2,7 +2,7 @@ "author": "Grafana Labs", "license": "Apache-2.0", "name": "@grafana/runtime", - "version": "12.2.0-pre", + "version": "12.3.0-pre", "description": "Grafana Runtime Library", "keywords": [ "grafana", @@ -53,11 +53,11 @@ "postpack": "mv package.json.bak package.json && rimraf ./unstable" }, "dependencies": { - "@grafana/data": "12.2.0-pre", - "@grafana/e2e-selectors": "12.2.0-pre", + "@grafana/data": "12.3.0-pre", + "@grafana/e2e-selectors": "12.3.0-pre", "@grafana/faro-web-sdk": "^1.13.2", - "@grafana/schema": "12.2.0-pre", - "@grafana/ui": "12.2.0-pre", + "@grafana/schema": "12.3.0-pre", + "@grafana/ui": "12.3.0-pre", "@types/systemjs": "6.15.3", "history": "4.10.1", "lodash": "4.17.21", diff --git a/packages/grafana-schema/package.json b/packages/grafana-schema/package.json index 159111d986b..773a4a663d2 100644 --- a/packages/grafana-schema/package.json +++ b/packages/grafana-schema/package.json @@ -2,7 +2,7 @@ "author": "Grafana Labs", "license": "Apache-2.0", "name": "@grafana/schema", - "version": "12.2.0-pre", + "version": "12.3.0-pre", "description": "Grafana Schema Library", "keywords": [ "typescript" diff --git a/packages/grafana-schema/src/raw/composable/annotationslist/panelcfg/x/AnnotationsListPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/annotationslist/panelcfg/x/AnnotationsListPanelCfg_types.gen.ts index 63bb4339949..c9d2e73432e 100644 --- a/packages/grafana-schema/src/raw/composable/annotationslist/panelcfg/x/AnnotationsListPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/annotationslist/panelcfg/x/AnnotationsListPanelCfg_types.gen.ts @@ -8,7 +8,7 @@ // // Run 'make gen-cue' from repository root to regenerate. -export const pluginVersion = "12.2.0-pre"; +export const pluginVersion = "12.3.0-pre"; export interface Options { limit: number; diff --git a/packages/grafana-schema/src/raw/composable/barchart/panelcfg/x/BarChartPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/barchart/panelcfg/x/BarChartPanelCfg_types.gen.ts index 59924a010ae..275d5d7c921 100644 --- a/packages/grafana-schema/src/raw/composable/barchart/panelcfg/x/BarChartPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/barchart/panelcfg/x/BarChartPanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as common from '@grafana/schema'; -export const pluginVersion = "12.2.0-pre"; +export const pluginVersion = "12.3.0-pre"; export interface Options extends common.OptionsWithLegend, common.OptionsWithTooltip, common.OptionsWithTextFormatting { /** diff --git a/packages/grafana-schema/src/raw/composable/bargauge/panelcfg/x/BarGaugePanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/bargauge/panelcfg/x/BarGaugePanelCfg_types.gen.ts index c086da202b8..dcf792aadbd 100644 --- a/packages/grafana-schema/src/raw/composable/bargauge/panelcfg/x/BarGaugePanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/bargauge/panelcfg/x/BarGaugePanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as common from '@grafana/schema'; -export const pluginVersion = "12.2.0-pre"; +export const pluginVersion = "12.3.0-pre"; export interface Options extends common.OptionsWithLegend, common.SingleStatBaseOptions { displayMode: common.BarGaugeDisplayMode; diff --git a/packages/grafana-schema/src/raw/composable/candlestick/panelcfg/x/CandlestickPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/candlestick/panelcfg/x/CandlestickPanelCfg_types.gen.ts index ea817787d65..ab38df01ee6 100644 --- a/packages/grafana-schema/src/raw/composable/candlestick/panelcfg/x/CandlestickPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/candlestick/panelcfg/x/CandlestickPanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as common from '@grafana/schema'; -export const pluginVersion = "12.2.0-pre"; +export const pluginVersion = "12.3.0-pre"; export enum VizDisplayMode { Candles = 'candles', diff --git a/packages/grafana-schema/src/raw/composable/canvas/panelcfg/x/CanvasPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/canvas/panelcfg/x/CanvasPanelCfg_types.gen.ts index 04fec030dfa..13939c556d6 100644 --- a/packages/grafana-schema/src/raw/composable/canvas/panelcfg/x/CanvasPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/canvas/panelcfg/x/CanvasPanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as ui from '@grafana/schema'; -export const pluginVersion = "12.2.0-pre"; +export const pluginVersion = "12.3.0-pre"; export enum HorizontalConstraint { Center = 'center', diff --git a/packages/grafana-schema/src/raw/composable/cloudwatch/dataquery/x/CloudWatchDataQuery_types.gen.ts b/packages/grafana-schema/src/raw/composable/cloudwatch/dataquery/x/CloudWatchDataQuery_types.gen.ts index f0e4394451d..2334d1d1efd 100644 --- a/packages/grafana-schema/src/raw/composable/cloudwatch/dataquery/x/CloudWatchDataQuery_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/cloudwatch/dataquery/x/CloudWatchDataQuery_types.gen.ts @@ -10,7 +10,7 @@ import * as common from '@grafana/schema'; -export const pluginVersion = "12.2.0-pre"; +export const pluginVersion = "12.3.0-pre"; export interface MetricStat { /** diff --git a/packages/grafana-schema/src/raw/composable/dashboardlist/panelcfg/x/DashboardListPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/dashboardlist/panelcfg/x/DashboardListPanelCfg_types.gen.ts index 1348c30dbad..a1ec65bf148 100644 --- a/packages/grafana-schema/src/raw/composable/dashboardlist/panelcfg/x/DashboardListPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/dashboardlist/panelcfg/x/DashboardListPanelCfg_types.gen.ts @@ -8,7 +8,7 @@ // // Run 'make gen-cue' from repository root to regenerate. -export const pluginVersion = "12.2.0-pre"; +export const pluginVersion = "12.3.0-pre"; export interface Options { /** diff --git a/packages/grafana-schema/src/raw/composable/datagrid/panelcfg/x/DatagridPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/datagrid/panelcfg/x/DatagridPanelCfg_types.gen.ts index d8db9ef7edf..65ef7e9883d 100644 --- a/packages/grafana-schema/src/raw/composable/datagrid/panelcfg/x/DatagridPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/datagrid/panelcfg/x/DatagridPanelCfg_types.gen.ts @@ -8,7 +8,7 @@ // // Run 'make gen-cue' from repository root to regenerate. -export const pluginVersion = "12.2.0-pre"; +export const pluginVersion = "12.3.0-pre"; export interface Options { selectedSeries: number; diff --git a/packages/grafana-schema/src/raw/composable/debug/panelcfg/x/DebugPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/debug/panelcfg/x/DebugPanelCfg_types.gen.ts index 811836dff43..443add70b78 100644 --- a/packages/grafana-schema/src/raw/composable/debug/panelcfg/x/DebugPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/debug/panelcfg/x/DebugPanelCfg_types.gen.ts @@ -8,7 +8,7 @@ // // Run 'make gen-cue' from repository root to regenerate. -export const pluginVersion = "12.2.0-pre"; +export const pluginVersion = "12.3.0-pre"; export type UpdateConfig = { render: boolean, diff --git a/packages/grafana-schema/src/raw/composable/elasticsearch/dataquery/x/ElasticsearchDataQuery_types.gen.ts b/packages/grafana-schema/src/raw/composable/elasticsearch/dataquery/x/ElasticsearchDataQuery_types.gen.ts index dfb50c2c981..d63d368d534 100644 --- a/packages/grafana-schema/src/raw/composable/elasticsearch/dataquery/x/ElasticsearchDataQuery_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/elasticsearch/dataquery/x/ElasticsearchDataQuery_types.gen.ts @@ -10,7 +10,7 @@ import * as common from '@grafana/schema'; -export const pluginVersion = "12.2.0-pre"; +export const pluginVersion = "12.3.0-pre"; export type BucketAggregation = (DateHistogram | Histogram | Terms | Filters | GeoHashGrid | Nested); diff --git a/packages/grafana-schema/src/raw/composable/gauge/panelcfg/x/GaugePanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/gauge/panelcfg/x/GaugePanelCfg_types.gen.ts index 36aa14da169..ac1fd6808c2 100644 --- a/packages/grafana-schema/src/raw/composable/gauge/panelcfg/x/GaugePanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/gauge/panelcfg/x/GaugePanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as common from '@grafana/schema'; -export const pluginVersion = "12.2.0-pre"; +export const pluginVersion = "12.3.0-pre"; export interface Options extends common.SingleStatBaseOptions { minVizHeight: number; diff --git a/packages/grafana-schema/src/raw/composable/geomap/panelcfg/x/GeomapPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/geomap/panelcfg/x/GeomapPanelCfg_types.gen.ts index fa1e3eaf299..4819a4cfc1b 100644 --- a/packages/grafana-schema/src/raw/composable/geomap/panelcfg/x/GeomapPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/geomap/panelcfg/x/GeomapPanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as ui from '@grafana/schema'; -export const pluginVersion = "12.2.0-pre"; +export const pluginVersion = "12.3.0-pre"; export interface Options { basemap: ui.MapLayerOptions; diff --git a/packages/grafana-schema/src/raw/composable/heatmap/panelcfg/x/HeatmapPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/heatmap/panelcfg/x/HeatmapPanelCfg_types.gen.ts index b0a2a2b5233..d905b1bd2ea 100644 --- a/packages/grafana-schema/src/raw/composable/heatmap/panelcfg/x/HeatmapPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/heatmap/panelcfg/x/HeatmapPanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as ui from '@grafana/schema'; -export const pluginVersion = "12.2.0-pre"; +export const pluginVersion = "12.3.0-pre"; /** * Controls the color mode of the heatmap diff --git a/packages/grafana-schema/src/raw/composable/histogram/panelcfg/x/HistogramPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/histogram/panelcfg/x/HistogramPanelCfg_types.gen.ts index 59b82823d78..2d5705e31e5 100644 --- a/packages/grafana-schema/src/raw/composable/histogram/panelcfg/x/HistogramPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/histogram/panelcfg/x/HistogramPanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as common from '@grafana/schema'; -export const pluginVersion = "12.2.0-pre"; +export const pluginVersion = "12.3.0-pre"; export interface Options extends common.OptionsWithLegend, common.OptionsWithTooltip { /** diff --git a/packages/grafana-schema/src/raw/composable/logs/panelcfg/x/LogsPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/logs/panelcfg/x/LogsPanelCfg_types.gen.ts index 5a048b59af0..4fc8ccf355a 100644 --- a/packages/grafana-schema/src/raw/composable/logs/panelcfg/x/LogsPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/logs/panelcfg/x/LogsPanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as common from '@grafana/schema'; -export const pluginVersion = "12.2.0-pre"; +export const pluginVersion = "12.3.0-pre"; export interface Options { controlsStorageKey?: string; diff --git a/packages/grafana-schema/src/raw/composable/news/panelcfg/x/NewsPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/news/panelcfg/x/NewsPanelCfg_types.gen.ts index eb0b74331d4..5f46545f820 100644 --- a/packages/grafana-schema/src/raw/composable/news/panelcfg/x/NewsPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/news/panelcfg/x/NewsPanelCfg_types.gen.ts @@ -8,7 +8,7 @@ // // Run 'make gen-cue' from repository root to regenerate. -export const pluginVersion = "12.2.0-pre"; +export const pluginVersion = "12.3.0-pre"; export interface Options { /** diff --git a/packages/grafana-schema/src/raw/composable/nodegraph/panelcfg/x/NodeGraphPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/nodegraph/panelcfg/x/NodeGraphPanelCfg_types.gen.ts index a5e4657464a..17fe57fcc44 100644 --- a/packages/grafana-schema/src/raw/composable/nodegraph/panelcfg/x/NodeGraphPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/nodegraph/panelcfg/x/NodeGraphPanelCfg_types.gen.ts @@ -8,7 +8,7 @@ // // Run 'make gen-cue' from repository root to regenerate. -export const pluginVersion = "12.2.0-pre"; +export const pluginVersion = "12.3.0-pre"; export interface ArcOption { /** diff --git a/packages/grafana-schema/src/raw/composable/piechart/panelcfg/x/PieChartPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/piechart/panelcfg/x/PieChartPanelCfg_types.gen.ts index 913fa45c9ac..067c6d7d146 100644 --- a/packages/grafana-schema/src/raw/composable/piechart/panelcfg/x/PieChartPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/piechart/panelcfg/x/PieChartPanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as common from '@grafana/schema'; -export const pluginVersion = "12.2.0-pre"; +export const pluginVersion = "12.3.0-pre"; /** * Select the pie chart display style. diff --git a/packages/grafana-schema/src/raw/composable/stat/panelcfg/x/StatPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/stat/panelcfg/x/StatPanelCfg_types.gen.ts index 8296f089a14..0fb48020d23 100644 --- a/packages/grafana-schema/src/raw/composable/stat/panelcfg/x/StatPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/stat/panelcfg/x/StatPanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as common from '@grafana/schema'; -export const pluginVersion = "12.2.0-pre"; +export const pluginVersion = "12.3.0-pre"; export interface Options extends common.SingleStatBaseOptions { colorMode: common.BigValueColorMode; diff --git a/packages/grafana-schema/src/raw/composable/statetimeline/panelcfg/x/StateTimelinePanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/statetimeline/panelcfg/x/StateTimelinePanelCfg_types.gen.ts index 58b487eb4d3..b8220d24d3a 100644 --- a/packages/grafana-schema/src/raw/composable/statetimeline/panelcfg/x/StateTimelinePanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/statetimeline/panelcfg/x/StateTimelinePanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as ui from '@grafana/schema'; -export const pluginVersion = "12.2.0-pre"; +export const pluginVersion = "12.3.0-pre"; export interface Options extends ui.OptionsWithLegend, ui.OptionsWithTooltip, ui.OptionsWithTimezones { /** diff --git a/packages/grafana-schema/src/raw/composable/statushistory/panelcfg/x/StatusHistoryPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/statushistory/panelcfg/x/StatusHistoryPanelCfg_types.gen.ts index 0ddf8a30c7a..030a310ff8b 100644 --- a/packages/grafana-schema/src/raw/composable/statushistory/panelcfg/x/StatusHistoryPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/statushistory/panelcfg/x/StatusHistoryPanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as ui from '@grafana/schema'; -export const pluginVersion = "12.2.0-pre"; +export const pluginVersion = "12.3.0-pre"; export interface Options extends ui.OptionsWithLegend, ui.OptionsWithTooltip, ui.OptionsWithTimezones { /** diff --git a/packages/grafana-schema/src/raw/composable/table/panelcfg/x/TablePanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/table/panelcfg/x/TablePanelCfg_types.gen.ts index aebc8fd7be4..3964c70f630 100644 --- a/packages/grafana-schema/src/raw/composable/table/panelcfg/x/TablePanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/table/panelcfg/x/TablePanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as ui from '@grafana/schema'; -export const pluginVersion = "12.2.0-pre"; +export const pluginVersion = "12.3.0-pre"; export interface Options { /** diff --git a/packages/grafana-schema/src/raw/composable/text/panelcfg/x/TextPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/text/panelcfg/x/TextPanelCfg_types.gen.ts index 4fbda90032b..39053ebb9af 100644 --- a/packages/grafana-schema/src/raw/composable/text/panelcfg/x/TextPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/text/panelcfg/x/TextPanelCfg_types.gen.ts @@ -8,7 +8,7 @@ // // Run 'make gen-cue' from repository root to regenerate. -export const pluginVersion = "12.2.0-pre"; +export const pluginVersion = "12.3.0-pre"; export enum TextMode { Code = 'code', diff --git a/packages/grafana-schema/src/raw/composable/timeseries/panelcfg/x/TimeSeriesPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/timeseries/panelcfg/x/TimeSeriesPanelCfg_types.gen.ts index 30349a9ff9c..538860716da 100644 --- a/packages/grafana-schema/src/raw/composable/timeseries/panelcfg/x/TimeSeriesPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/timeseries/panelcfg/x/TimeSeriesPanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as common from '@grafana/schema'; -export const pluginVersion = "12.2.0-pre"; +export const pluginVersion = "12.3.0-pre"; export interface Options extends common.OptionsWithTimezones { legend: common.VizLegendOptions; diff --git a/packages/grafana-schema/src/raw/composable/trend/panelcfg/x/TrendPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/trend/panelcfg/x/TrendPanelCfg_types.gen.ts index aa3b48c20ab..7ec6ddd5686 100644 --- a/packages/grafana-schema/src/raw/composable/trend/panelcfg/x/TrendPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/trend/panelcfg/x/TrendPanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as common from '@grafana/schema'; -export const pluginVersion = "12.2.0-pre"; +export const pluginVersion = "12.3.0-pre"; /** * Identical to timeseries... except it does not have timezone settings diff --git a/packages/grafana-schema/src/raw/composable/xychart/panelcfg/x/XYChartPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/xychart/panelcfg/x/XYChartPanelCfg_types.gen.ts index 13516a37619..450721304fe 100644 --- a/packages/grafana-schema/src/raw/composable/xychart/panelcfg/x/XYChartPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/xychart/panelcfg/x/XYChartPanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as common from '@grafana/schema'; -export const pluginVersion = "12.2.0-pre"; +export const pluginVersion = "12.3.0-pre"; export enum PointShape { Circle = 'circle', diff --git a/packages/grafana-sql/package.json b/packages/grafana-sql/package.json index d15ad0a5df6..a0206458ad3 100644 --- a/packages/grafana-sql/package.json +++ b/packages/grafana-sql/package.json @@ -3,7 +3,7 @@ "license": "AGPL-3.0-only", "private": true, "name": "@grafana/sql", - "version": "12.2.0-pre", + "version": "12.3.0-pre", "repository": { "type": "git", "url": "http://github.com/grafana/grafana.git", @@ -16,12 +16,12 @@ }, "dependencies": { "@emotion/css": "11.13.5", - "@grafana/data": "12.2.0-pre", - "@grafana/e2e-selectors": "12.2.0-pre", - "@grafana/i18n": "12.2.0-pre", + "@grafana/data": "12.3.0-pre", + "@grafana/e2e-selectors": "12.3.0-pre", + "@grafana/i18n": "12.3.0-pre", "@grafana/plugin-ui": "^0.10.10", - "@grafana/runtime": "12.2.0-pre", - "@grafana/ui": "12.2.0-pre", + "@grafana/runtime": "12.3.0-pre", + "@grafana/ui": "12.3.0-pre", "@react-awesome-query-builder/ui": "6.6.15", "immutable": "5.1.3", "lodash": "4.17.21", diff --git a/packages/grafana-test-utils/package.json b/packages/grafana-test-utils/package.json index 842cc9d6517..60e375d77cb 100644 --- a/packages/grafana-test-utils/package.json +++ b/packages/grafana-test-utils/package.json @@ -2,7 +2,7 @@ "author": "Grafana Labs", "license": "Apache-2.0", "name": "@grafana/test-utils", - "version": "12.2.0-pre", + "version": "12.3.0-pre", "private": true, "description": "Grafana test utils & Mock API", "keywords": [ diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index fb1a899ca6e..25f637bbff7 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -2,7 +2,7 @@ "author": "Grafana Labs", "license": "Apache-2.0", "name": "@grafana/ui", - "version": "12.2.0-pre", + "version": "12.3.0-pre", "description": "Grafana Components Library", "keywords": [ "grafana", @@ -67,11 +67,11 @@ "@emotion/react": "11.14.0", "@emotion/serialize": "1.3.3", "@floating-ui/react": "0.27.16", - "@grafana/data": "12.2.0-pre", - "@grafana/e2e-selectors": "12.2.0-pre", + "@grafana/data": "12.3.0-pre", + "@grafana/e2e-selectors": "12.3.0-pre", "@grafana/faro-web-sdk": "^1.13.2", - "@grafana/i18n": "12.2.0-pre", - "@grafana/schema": "12.2.0-pre", + "@grafana/i18n": "12.3.0-pre", + "@grafana/schema": "12.3.0-pre", "@hello-pangea/dnd": "18.0.1", "@monaco-editor/react": "4.7.0", "@popperjs/core": "2.11.8", diff --git a/public/app/plugins/datasource/azuremonitor/package.json b/public/app/plugins/datasource/azuremonitor/package.json index e74bbab3853..a3d172bdfee 100644 --- a/public/app/plugins/datasource/azuremonitor/package.json +++ b/public/app/plugins/datasource/azuremonitor/package.json @@ -2,15 +2,15 @@ "name": "@grafana-plugins/grafana-azure-monitor-datasource", "description": "Grafana data source for Azure Monitor", "private": true, - "version": "12.2.0-pre", + "version": "12.3.0-pre", "dependencies": { "@emotion/css": "11.13.5", - "@grafana/data": "12.2.0-pre", - "@grafana/i18n": "12.2.0-pre", + "@grafana/data": "12.3.0-pre", + "@grafana/i18n": "12.3.0-pre", "@grafana/plugin-ui": "^0.10.10", - "@grafana/runtime": "12.2.0-pre", - "@grafana/schema": "12.2.0-pre", - "@grafana/ui": "12.2.0-pre", + "@grafana/runtime": "12.3.0-pre", + "@grafana/schema": "12.3.0-pre", + "@grafana/ui": "12.3.0-pre", "@kusto/monaco-kusto": "^10.0.0", "fast-deep-equal": "^3.1.3", "i18next": "^25.0.0", @@ -26,8 +26,8 @@ "tslib": "2.8.1" }, "devDependencies": { - "@grafana/e2e-selectors": "12.2.0-pre", - "@grafana/plugin-configs": "12.2.0-pre", + "@grafana/e2e-selectors": "12.3.0-pre", + "@grafana/plugin-configs": "12.3.0-pre", "@testing-library/dom": "10.4.1", "@testing-library/jest-dom": "6.6.4", "@testing-library/react": "16.3.0", diff --git a/public/app/plugins/datasource/cloud-monitoring/package.json b/public/app/plugins/datasource/cloud-monitoring/package.json index 080e3faab65..34eab1a19fb 100644 --- a/public/app/plugins/datasource/cloud-monitoring/package.json +++ b/public/app/plugins/datasource/cloud-monitoring/package.json @@ -2,15 +2,15 @@ "name": "@grafana-plugins/stackdriver", "description": "Grafana data source for Google Cloud Monitoring", "private": true, - "version": "12.2.0-pre", + "version": "12.3.0-pre", "dependencies": { "@emotion/css": "11.13.5", - "@grafana/data": "12.2.0-pre", + "@grafana/data": "12.3.0-pre", "@grafana/google-sdk": "0.3.4", "@grafana/plugin-ui": "^0.10.10", - "@grafana/runtime": "12.2.0-pre", - "@grafana/schema": "12.2.0-pre", - "@grafana/ui": "12.2.0-pre", + "@grafana/runtime": "12.3.0-pre", + "@grafana/schema": "12.3.0-pre", + "@grafana/ui": "12.3.0-pre", "debounce-promise": "3.1.2", "fast-deep-equal": "^3.1.3", "i18next": "^25.0.0", @@ -26,8 +26,8 @@ "tslib": "2.8.1" }, "devDependencies": { - "@grafana/e2e-selectors": "12.2.0-pre", - "@grafana/plugin-configs": "12.2.0-pre", + "@grafana/e2e-selectors": "12.3.0-pre", + "@grafana/plugin-configs": "12.3.0-pre", "@testing-library/dom": "10.4.1", "@testing-library/jest-dom": "6.6.4", "@testing-library/react": "16.3.0", diff --git a/public/app/plugins/datasource/grafana-postgresql-datasource/package.json b/public/app/plugins/datasource/grafana-postgresql-datasource/package.json index 7994c090a5e..66dff7a32ce 100644 --- a/public/app/plugins/datasource/grafana-postgresql-datasource/package.json +++ b/public/app/plugins/datasource/grafana-postgresql-datasource/package.json @@ -2,22 +2,22 @@ "name": "@grafana-plugins/grafana-postgresql-datasource", "description": "PostgreSQL data source plugin", "private": true, - "version": "12.2.0-pre", + "version": "12.3.0-pre", "dependencies": { "@emotion/css": "11.13.5", - "@grafana/data": "12.2.0-pre", + "@grafana/data": "12.3.0-pre", "@grafana/plugin-ui": "^0.10.10", - "@grafana/runtime": "12.2.0-pre", - "@grafana/sql": "12.2.0-pre", - "@grafana/ui": "12.2.0-pre", + "@grafana/runtime": "12.3.0-pre", + "@grafana/sql": "12.3.0-pre", + "@grafana/ui": "12.3.0-pre", "lodash": "4.17.21", "react": "18.3.1", "rxjs": "7.8.2", "tslib": "2.8.1" }, "devDependencies": { - "@grafana/e2e-selectors": "12.2.0-pre", - "@grafana/plugin-configs": "12.2.0-pre", + "@grafana/e2e-selectors": "12.3.0-pre", + "@grafana/plugin-configs": "12.3.0-pre", "@testing-library/dom": "10.4.1", "@testing-library/react": "16.3.0", "@testing-library/user-event": "14.6.1", diff --git a/public/app/plugins/datasource/grafana-pyroscope-datasource/package.json b/public/app/plugins/datasource/grafana-pyroscope-datasource/package.json index 6ddf061952d..44e8655d367 100644 --- a/public/app/plugins/datasource/grafana-pyroscope-datasource/package.json +++ b/public/app/plugins/datasource/grafana-pyroscope-datasource/package.json @@ -2,13 +2,13 @@ "name": "@grafana-plugins/grafana-pyroscope-datasource", "description": "Continuous profiling for analysis of CPU and memory usage, down to the line number and throughout time. Saving infrastructure cost, improving performance, and increasing reliability.", "private": true, - "version": "12.2.0-pre", + "version": "12.3.0-pre", "dependencies": { "@emotion/css": "11.13.5", - "@grafana/data": "12.2.0-pre", - "@grafana/runtime": "12.2.0-pre", - "@grafana/schema": "12.2.0-pre", - "@grafana/ui": "12.2.0-pre", + "@grafana/data": "12.3.0-pre", + "@grafana/runtime": "12.3.0-pre", + "@grafana/schema": "12.3.0-pre", + "@grafana/ui": "12.3.0-pre", "fast-deep-equal": "^3.1.3", "lodash": "4.17.21", "monaco-editor": "0.34.1", @@ -20,7 +20,7 @@ "tslib": "2.8.1" }, "devDependencies": { - "@grafana/plugin-configs": "12.2.0-pre", + "@grafana/plugin-configs": "12.3.0-pre", "@testing-library/dom": "10.4.1", "@testing-library/jest-dom": "6.6.4", "@testing-library/react": "16.3.0", diff --git a/public/app/plugins/datasource/grafana-testdata-datasource/package.json b/public/app/plugins/datasource/grafana-testdata-datasource/package.json index 544457c4a16..bd5077f4294 100644 --- a/public/app/plugins/datasource/grafana-testdata-datasource/package.json +++ b/public/app/plugins/datasource/grafana-testdata-datasource/package.json @@ -2,13 +2,13 @@ "name": "@grafana-plugins/grafana-testdata-datasource", "description": "Generates test data in different forms", "private": true, - "version": "12.2.0-pre", + "version": "12.3.0-pre", "dependencies": { "@emotion/css": "11.13.5", - "@grafana/data": "12.2.0-pre", - "@grafana/runtime": "12.2.0-pre", - "@grafana/schema": "12.2.0-pre", - "@grafana/ui": "12.2.0-pre", + "@grafana/data": "12.3.0-pre", + "@grafana/runtime": "12.3.0-pre", + "@grafana/schema": "12.3.0-pre", + "@grafana/ui": "12.3.0-pre", "d3-random": "^3.0.1", "lodash": "4.17.21", "micro-memoize": "^4.1.2", @@ -21,8 +21,8 @@ "uuid": "11.1.0" }, "devDependencies": { - "@grafana/e2e-selectors": "12.2.0-pre", - "@grafana/plugin-configs": "12.2.0-pre", + "@grafana/e2e-selectors": "12.3.0-pre", + "@grafana/plugin-configs": "12.3.0-pre", "@testing-library/dom": "10.4.1", "@testing-library/jest-dom": "6.6.4", "@testing-library/react": "16.3.0", diff --git a/public/app/plugins/datasource/graphite/package.json b/public/app/plugins/datasource/graphite/package.json index ca3d33b93d9..2b7e0ec4eda 100644 --- a/public/app/plugins/datasource/graphite/package.json +++ b/public/app/plugins/datasource/graphite/package.json @@ -2,14 +2,14 @@ "name": "@grafana-plugins/graphite", "description": "Graphite data source plugin for Grafana", "private": true, - "version": "12.2.0-pre", + "version": "12.3.0-pre", "dependencies": { "@emotion/css": "11.13.5", - "@grafana/data": "12.2.0-pre", + "@grafana/data": "12.3.0-pre", "@grafana/plugin-ui": "^0.10.10", - "@grafana/runtime": "12.2.0-pre", - "@grafana/schema": "12.2.0-pre", - "@grafana/ui": "12.2.0-pre", + "@grafana/runtime": "12.3.0-pre", + "@grafana/schema": "12.3.0-pre", + "@grafana/ui": "12.3.0-pre", "@reduxjs/toolkit": "2.8.2", "lodash": "4.17.21", "moment": "2.30.1", @@ -23,8 +23,8 @@ "uuid": "11.1.0" }, "devDependencies": { - "@grafana/e2e-selectors": "12.2.0-pre", - "@grafana/plugin-configs": "12.2.0-pre", + "@grafana/e2e-selectors": "12.3.0-pre", + "@grafana/plugin-configs": "12.3.0-pre", "@testing-library/dom": "10.4.1", "@testing-library/jest-dom": "6.6.4", "@testing-library/react": "16.3.0", diff --git a/public/app/plugins/datasource/jaeger/package.json b/public/app/plugins/datasource/jaeger/package.json index deccbf6277b..cee33f17a4d 100644 --- a/public/app/plugins/datasource/jaeger/package.json +++ b/public/app/plugins/datasource/jaeger/package.json @@ -2,7 +2,7 @@ "name": "@grafana-plugins/jaeger", "description": "Jaeger plugin for Grafana", "private": true, - "version": "12.2.0-pre", + "version": "12.3.0-pre", "dependencies": { "@emotion/css": "11.13.5", "@grafana/data": "workspace:*", diff --git a/public/app/plugins/datasource/loki/package.json b/public/app/plugins/datasource/loki/package.json index b98404726cd..abdc101521d 100644 --- a/public/app/plugins/datasource/loki/package.json +++ b/public/app/plugins/datasource/loki/package.json @@ -2,16 +2,16 @@ "name": "@grafana-plugins/loki", "description": "Loki data source plugin for Grafana", "private": true, - "version": "12.2.0-pre", + "version": "12.3.0-pre", "dependencies": { "@emotion/css": "11.13.5", - "@grafana/data": "12.2.0-pre", + "@grafana/data": "12.3.0-pre", "@grafana/lezer-logql": "0.2.8", "@grafana/llm": "0.22.1", "@grafana/monaco-logql": "^0.0.8", - "@grafana/runtime": "12.2.0-pre", - "@grafana/schema": "12.2.0-pre", - "@grafana/ui": "12.2.0-pre", + "@grafana/runtime": "12.3.0-pre", + "@grafana/schema": "12.3.0-pre", + "@grafana/ui": "12.3.0-pre", "d3-random": "^3.0.1", "lodash": "4.17.21", "micro-memoize": "^4.1.2", @@ -24,8 +24,8 @@ "uuid": "11.1.0" }, "devDependencies": { - "@grafana/e2e-selectors": "12.2.0-pre", - "@grafana/plugin-configs": "12.2.0-pre", + "@grafana/e2e-selectors": "12.3.0-pre", + "@grafana/plugin-configs": "12.3.0-pre", "@testing-library/dom": "10.4.1", "@testing-library/jest-dom": "6.6.4", "@testing-library/react": "16.3.0", diff --git a/public/app/plugins/datasource/mssql/package.json b/public/app/plugins/datasource/mssql/package.json index a818c7e39ec..4ff7632191b 100644 --- a/public/app/plugins/datasource/mssql/package.json +++ b/public/app/plugins/datasource/mssql/package.json @@ -2,23 +2,23 @@ "name": "@grafana-plugins/mssql", "description": "MSSQL data source plugin", "private": true, - "version": "12.2.0-pre", + "version": "12.3.0-pre", "dependencies": { "@emotion/css": "11.13.5", - "@grafana/data": "12.2.0-pre", - "@grafana/i18n": "12.2.0-pre", + "@grafana/data": "12.3.0-pre", + "@grafana/i18n": "12.3.0-pre", "@grafana/plugin-ui": "^0.10.10", - "@grafana/runtime": "12.2.0-pre", - "@grafana/sql": "12.2.0-pre", - "@grafana/ui": "12.2.0-pre", + "@grafana/runtime": "12.3.0-pre", + "@grafana/sql": "12.3.0-pre", + "@grafana/ui": "12.3.0-pre", "lodash": "4.17.21", "react": "18.3.1", "rxjs": "7.8.2", "tslib": "2.8.1" }, "devDependencies": { - "@grafana/e2e-selectors": "12.2.0-pre", - "@grafana/plugin-configs": "12.2.0-pre", + "@grafana/e2e-selectors": "12.3.0-pre", + "@grafana/plugin-configs": "12.3.0-pre", "@testing-library/dom": "10.4.1", "@testing-library/react": "16.3.0", "@testing-library/user-event": "14.6.1", diff --git a/public/app/plugins/datasource/mysql/package.json b/public/app/plugins/datasource/mysql/package.json index a7d528a2856..ba0622abbbe 100644 --- a/public/app/plugins/datasource/mysql/package.json +++ b/public/app/plugins/datasource/mysql/package.json @@ -2,22 +2,22 @@ "name": "@grafana-plugins/mysql", "description": "MySQL data source plugin", "private": true, - "version": "12.2.0-pre", + "version": "12.3.0-pre", "dependencies": { "@emotion/css": "11.13.5", - "@grafana/data": "12.2.0-pre", + "@grafana/data": "12.3.0-pre", "@grafana/plugin-ui": "^0.10.10", - "@grafana/runtime": "12.2.0-pre", - "@grafana/sql": "12.2.0-pre", - "@grafana/ui": "12.2.0-pre", + "@grafana/runtime": "12.3.0-pre", + "@grafana/sql": "12.3.0-pre", + "@grafana/ui": "12.3.0-pre", "lodash": "4.17.21", "react": "18.3.1", "rxjs": "7.8.2", "tslib": "2.8.1" }, "devDependencies": { - "@grafana/e2e-selectors": "12.2.0-pre", - "@grafana/plugin-configs": "12.2.0-pre", + "@grafana/e2e-selectors": "12.3.0-pre", + "@grafana/plugin-configs": "12.3.0-pre", "@testing-library/dom": "10.4.1", "@testing-library/react": "16.3.0", "@testing-library/user-event": "14.6.1", diff --git a/public/app/plugins/datasource/parca/package.json b/public/app/plugins/datasource/parca/package.json index 3b08680a41a..2adf3dd538f 100644 --- a/public/app/plugins/datasource/parca/package.json +++ b/public/app/plugins/datasource/parca/package.json @@ -2,13 +2,13 @@ "name": "@grafana-plugins/parca", "description": "Continuous profiling for analysis of CPU and memory usage, down to the line number and throughout time. Saving infrastructure cost, improving performance, and increasing reliability.", "private": true, - "version": "12.2.0-pre", + "version": "12.3.0-pre", "dependencies": { "@emotion/css": "11.13.5", - "@grafana/data": "12.2.0-pre", - "@grafana/runtime": "12.2.0-pre", - "@grafana/schema": "12.2.0-pre", - "@grafana/ui": "12.2.0-pre", + "@grafana/data": "12.3.0-pre", + "@grafana/runtime": "12.3.0-pre", + "@grafana/schema": "12.3.0-pre", + "@grafana/ui": "12.3.0-pre", "lodash": "4.17.21", "monaco-editor": "0.34.1", "react": "18.3.1", @@ -18,7 +18,7 @@ "tslib": "2.8.1" }, "devDependencies": { - "@grafana/plugin-configs": "12.2.0-pre", + "@grafana/plugin-configs": "12.3.0-pre", "@testing-library/dom": "10.4.1", "@testing-library/react": "16.3.0", "@testing-library/user-event": "14.6.1", diff --git a/public/app/plugins/datasource/tempo/package.json b/public/app/plugins/datasource/tempo/package.json index f157bd5254d..fac7933eb79 100644 --- a/public/app/plugins/datasource/tempo/package.json +++ b/public/app/plugins/datasource/tempo/package.json @@ -2,7 +2,7 @@ "name": "@grafana-plugins/tempo", "description": "Grafana plugin for the Tempo data source.", "private": true, - "version": "12.2.0-pre", + "version": "12.3.0-pre", "dependencies": { "@emotion/css": "11.13.5", "@grafana/data": "workspace:*", @@ -38,7 +38,7 @@ "uuid": "11.1.0" }, "devDependencies": { - "@grafana/plugin-configs": "12.2.0-pre", + "@grafana/plugin-configs": "12.3.0-pre", "@testing-library/dom": "10.4.1", "@testing-library/jest-dom": "6.6.4", "@testing-library/react": "16.3.0", diff --git a/public/app/plugins/datasource/zipkin/package.json b/public/app/plugins/datasource/zipkin/package.json index 68e76e39fce..1b368d16eaf 100644 --- a/public/app/plugins/datasource/zipkin/package.json +++ b/public/app/plugins/datasource/zipkin/package.json @@ -2,7 +2,7 @@ "name": "@grafana-plugins/zipkin", "description": "Zipkin plugin for Grafana", "private": true, - "version": "12.2.0-pre", + "version": "12.3.0-pre", "dependencies": { "@emotion/css": "11.13.5", "@grafana/data": "workspace:*", diff --git a/yarn.lock b/yarn.lock index 5759752061e..91c46d31c3c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2426,14 +2426,14 @@ __metadata: resolution: "@grafana-plugins/grafana-azure-monitor-datasource@workspace:public/app/plugins/datasource/azuremonitor" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:12.2.0-pre" - "@grafana/e2e-selectors": "npm:12.2.0-pre" - "@grafana/i18n": "npm:12.2.0-pre" - "@grafana/plugin-configs": "npm:12.2.0-pre" + "@grafana/data": "npm:12.3.0-pre" + "@grafana/e2e-selectors": "npm:12.3.0-pre" + "@grafana/i18n": "npm:12.3.0-pre" + "@grafana/plugin-configs": "npm:12.3.0-pre" "@grafana/plugin-ui": "npm:^0.10.10" - "@grafana/runtime": "npm:12.2.0-pre" - "@grafana/schema": "npm:12.2.0-pre" - "@grafana/ui": "npm:12.2.0-pre" + "@grafana/runtime": "npm:12.3.0-pre" + "@grafana/schema": "npm:12.3.0-pre" + "@grafana/ui": "npm:12.3.0-pre" "@kusto/monaco-kusto": "npm:^10.0.0" "@testing-library/dom": "npm:10.4.1" "@testing-library/jest-dom": "npm:6.6.4" @@ -2473,13 +2473,13 @@ __metadata: resolution: "@grafana-plugins/grafana-postgresql-datasource@workspace:public/app/plugins/datasource/grafana-postgresql-datasource" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:12.2.0-pre" - "@grafana/e2e-selectors": "npm:12.2.0-pre" - "@grafana/plugin-configs": "npm:12.2.0-pre" + "@grafana/data": "npm:12.3.0-pre" + "@grafana/e2e-selectors": "npm:12.3.0-pre" + "@grafana/plugin-configs": "npm:12.3.0-pre" "@grafana/plugin-ui": "npm:^0.10.10" - "@grafana/runtime": "npm:12.2.0-pre" - "@grafana/sql": "npm:12.2.0-pre" - "@grafana/ui": "npm:12.2.0-pre" + "@grafana/runtime": "npm:12.3.0-pre" + "@grafana/sql": "npm:12.3.0-pre" + "@grafana/ui": "npm:12.3.0-pre" "@testing-library/dom": "npm:10.4.1" "@testing-library/react": "npm:16.3.0" "@testing-library/user-event": "npm:14.6.1" @@ -2505,11 +2505,11 @@ __metadata: resolution: "@grafana-plugins/grafana-pyroscope-datasource@workspace:public/app/plugins/datasource/grafana-pyroscope-datasource" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:12.2.0-pre" - "@grafana/plugin-configs": "npm:12.2.0-pre" - "@grafana/runtime": "npm:12.2.0-pre" - "@grafana/schema": "npm:12.2.0-pre" - "@grafana/ui": "npm:12.2.0-pre" + "@grafana/data": "npm:12.3.0-pre" + "@grafana/plugin-configs": "npm:12.3.0-pre" + "@grafana/runtime": "npm:12.3.0-pre" + "@grafana/schema": "npm:12.3.0-pre" + "@grafana/ui": "npm:12.3.0-pre" "@testing-library/dom": "npm:10.4.1" "@testing-library/jest-dom": "npm:6.6.4" "@testing-library/react": "npm:16.3.0" @@ -2546,12 +2546,12 @@ __metadata: resolution: "@grafana-plugins/grafana-testdata-datasource@workspace:public/app/plugins/datasource/grafana-testdata-datasource" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:12.2.0-pre" - "@grafana/e2e-selectors": "npm:12.2.0-pre" - "@grafana/plugin-configs": "npm:12.2.0-pre" - "@grafana/runtime": "npm:12.2.0-pre" - "@grafana/schema": "npm:12.2.0-pre" - "@grafana/ui": "npm:12.2.0-pre" + "@grafana/data": "npm:12.3.0-pre" + "@grafana/e2e-selectors": "npm:12.3.0-pre" + "@grafana/plugin-configs": "npm:12.3.0-pre" + "@grafana/runtime": "npm:12.3.0-pre" + "@grafana/schema": "npm:12.3.0-pre" + "@grafana/ui": "npm:12.3.0-pre" "@testing-library/dom": "npm:10.4.1" "@testing-library/jest-dom": "npm:6.6.4" "@testing-library/react": "npm:16.3.0" @@ -2587,13 +2587,13 @@ __metadata: resolution: "@grafana-plugins/graphite@workspace:public/app/plugins/datasource/graphite" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:12.2.0-pre" - "@grafana/e2e-selectors": "npm:12.2.0-pre" - "@grafana/plugin-configs": "npm:12.2.0-pre" + "@grafana/data": "npm:12.3.0-pre" + "@grafana/e2e-selectors": "npm:12.3.0-pre" + "@grafana/plugin-configs": "npm:12.3.0-pre" "@grafana/plugin-ui": "npm:^0.10.10" - "@grafana/runtime": "npm:12.2.0-pre" - "@grafana/schema": "npm:12.2.0-pre" - "@grafana/ui": "npm:12.2.0-pre" + "@grafana/runtime": "npm:12.3.0-pre" + "@grafana/schema": "npm:12.3.0-pre" + "@grafana/ui": "npm:12.3.0-pre" "@reduxjs/toolkit": "npm:2.8.2" "@testing-library/dom": "npm:10.4.1" "@testing-library/jest-dom": "npm:6.6.4" @@ -2673,15 +2673,15 @@ __metadata: resolution: "@grafana-plugins/loki@workspace:public/app/plugins/datasource/loki" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:12.2.0-pre" - "@grafana/e2e-selectors": "npm:12.2.0-pre" + "@grafana/data": "npm:12.3.0-pre" + "@grafana/e2e-selectors": "npm:12.3.0-pre" "@grafana/lezer-logql": "npm:0.2.8" "@grafana/llm": "npm:0.22.1" "@grafana/monaco-logql": "npm:^0.0.8" - "@grafana/plugin-configs": "npm:12.2.0-pre" - "@grafana/runtime": "npm:12.2.0-pre" - "@grafana/schema": "npm:12.2.0-pre" - "@grafana/ui": "npm:12.2.0-pre" + "@grafana/plugin-configs": "npm:12.3.0-pre" + "@grafana/runtime": "npm:12.3.0-pre" + "@grafana/schema": "npm:12.3.0-pre" + "@grafana/ui": "npm:12.3.0-pre" "@testing-library/dom": "npm:10.4.1" "@testing-library/jest-dom": "npm:6.6.4" "@testing-library/react": "npm:16.3.0" @@ -2717,14 +2717,14 @@ __metadata: resolution: "@grafana-plugins/mssql@workspace:public/app/plugins/datasource/mssql" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:12.2.0-pre" - "@grafana/e2e-selectors": "npm:12.2.0-pre" - "@grafana/i18n": "npm:12.2.0-pre" - "@grafana/plugin-configs": "npm:12.2.0-pre" + "@grafana/data": "npm:12.3.0-pre" + "@grafana/e2e-selectors": "npm:12.3.0-pre" + "@grafana/i18n": "npm:12.3.0-pre" + "@grafana/plugin-configs": "npm:12.3.0-pre" "@grafana/plugin-ui": "npm:^0.10.10" - "@grafana/runtime": "npm:12.2.0-pre" - "@grafana/sql": "npm:12.2.0-pre" - "@grafana/ui": "npm:12.2.0-pre" + "@grafana/runtime": "npm:12.3.0-pre" + "@grafana/sql": "npm:12.3.0-pre" + "@grafana/ui": "npm:12.3.0-pre" "@testing-library/dom": "npm:10.4.1" "@testing-library/react": "npm:16.3.0" "@testing-library/user-event": "npm:14.6.1" @@ -2750,13 +2750,13 @@ __metadata: resolution: "@grafana-plugins/mysql@workspace:public/app/plugins/datasource/mysql" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:12.2.0-pre" - "@grafana/e2e-selectors": "npm:12.2.0-pre" - "@grafana/plugin-configs": "npm:12.2.0-pre" + "@grafana/data": "npm:12.3.0-pre" + "@grafana/e2e-selectors": "npm:12.3.0-pre" + "@grafana/plugin-configs": "npm:12.3.0-pre" "@grafana/plugin-ui": "npm:^0.10.10" - "@grafana/runtime": "npm:12.2.0-pre" - "@grafana/sql": "npm:12.2.0-pre" - "@grafana/ui": "npm:12.2.0-pre" + "@grafana/runtime": "npm:12.3.0-pre" + "@grafana/sql": "npm:12.3.0-pre" + "@grafana/ui": "npm:12.3.0-pre" "@testing-library/dom": "npm:10.4.1" "@testing-library/react": "npm:16.3.0" "@testing-library/user-event": "npm:14.6.1" @@ -2782,11 +2782,11 @@ __metadata: resolution: "@grafana-plugins/parca@workspace:public/app/plugins/datasource/parca" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:12.2.0-pre" - "@grafana/plugin-configs": "npm:12.2.0-pre" - "@grafana/runtime": "npm:12.2.0-pre" - "@grafana/schema": "npm:12.2.0-pre" - "@grafana/ui": "npm:12.2.0-pre" + "@grafana/data": "npm:12.3.0-pre" + "@grafana/plugin-configs": "npm:12.3.0-pre" + "@grafana/runtime": "npm:12.3.0-pre" + "@grafana/schema": "npm:12.3.0-pre" + "@grafana/ui": "npm:12.3.0-pre" "@testing-library/dom": "npm:10.4.1" "@testing-library/react": "npm:16.3.0" "@testing-library/user-event": "npm:14.6.1" @@ -2815,14 +2815,14 @@ __metadata: resolution: "@grafana-plugins/stackdriver@workspace:public/app/plugins/datasource/cloud-monitoring" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:12.2.0-pre" - "@grafana/e2e-selectors": "npm:12.2.0-pre" + "@grafana/data": "npm:12.3.0-pre" + "@grafana/e2e-selectors": "npm:12.3.0-pre" "@grafana/google-sdk": "npm:0.3.4" - "@grafana/plugin-configs": "npm:12.2.0-pre" + "@grafana/plugin-configs": "npm:12.3.0-pre" "@grafana/plugin-ui": "npm:^0.10.10" - "@grafana/runtime": "npm:12.2.0-pre" - "@grafana/schema": "npm:12.2.0-pre" - "@grafana/ui": "npm:12.2.0-pre" + "@grafana/runtime": "npm:12.3.0-pre" + "@grafana/schema": "npm:12.3.0-pre" + "@grafana/ui": "npm:12.3.0-pre" "@testing-library/dom": "npm:10.4.1" "@testing-library/jest-dom": "npm:6.6.4" "@testing-library/react": "npm:16.3.0" @@ -2867,7 +2867,7 @@ __metadata: "@grafana/lezer-traceql": "npm:0.0.23" "@grafana/monaco-logql": "npm:^0.0.8" "@grafana/o11y-ds-frontend": "workspace:*" - "@grafana/plugin-configs": "npm:12.2.0-pre" + "@grafana/plugin-configs": "npm:12.3.0-pre" "@grafana/plugin-ui": "npm:^0.10.10" "@grafana/runtime": "workspace:*" "@grafana/schema": "workspace:*" @@ -2959,7 +2959,7 @@ __metadata: dependencies: "@emotion/css": "npm:11.13.5" "@faker-js/faker": "npm:^9.8.0" - "@grafana/i18n": "npm:12.2.0-pre" + "@grafana/i18n": "npm:12.3.0-pre" "@grafana/test-utils": "workspace:*" "@rtk-query/codegen-openapi": "npm:^2.0.0" "@testing-library/jest-dom": "npm:^6.6.3" @@ -3035,13 +3035,13 @@ __metadata: languageName: node linkType: hard -"@grafana/data@npm:12.2.0-pre, @grafana/data@workspace:*, @grafana/data@workspace:packages/grafana-data": +"@grafana/data@npm:12.3.0-pre, @grafana/data@workspace:*, @grafana/data@workspace:packages/grafana-data": version: 0.0.0-use.local resolution: "@grafana/data@workspace:packages/grafana-data" dependencies: "@braintree/sanitize-url": "npm:7.0.1" - "@grafana/i18n": "npm:12.2.0-pre" - "@grafana/schema": "npm:12.2.0-pre" + "@grafana/i18n": "npm:12.3.0-pre" + "@grafana/schema": "npm:12.3.0-pre" "@leeoniya/ufuzzy": "npm:1.0.18" "@rollup/plugin-node-resolve": "npm:16.0.1" "@types/d3-interpolate": "npm:^3.0.0" @@ -3088,7 +3088,7 @@ __metadata: languageName: unknown linkType: soft -"@grafana/e2e-selectors@npm:12.2.0-pre, @grafana/e2e-selectors@workspace:*, @grafana/e2e-selectors@workspace:packages/grafana-e2e-selectors": +"@grafana/e2e-selectors@npm:12.3.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" dependencies: @@ -3188,8 +3188,8 @@ __metadata: "@babel/preset-env": "npm:7.28.0" "@babel/preset-react": "npm:7.27.1" "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:12.2.0-pre" - "@grafana/ui": "npm:12.2.0-pre" + "@grafana/data": "npm:12.3.0-pre" + "@grafana/ui": "npm:12.3.0-pre" "@leeoniya/ufuzzy": "npm:1.0.18" "@rollup/plugin-node-resolve": "npm:16.0.1" "@testing-library/dom": "npm:10.4.1" @@ -3239,7 +3239,7 @@ __metadata: languageName: node linkType: hard -"@grafana/i18n@npm:12.2.0-pre, @grafana/i18n@workspace:*, @grafana/i18n@workspace:packages/grafana-i18n": +"@grafana/i18n@npm:12.3.0-pre, @grafana/i18n@workspace:*, @grafana/i18n@workspace:packages/grafana-i18n": version: 0.0.0-use.local resolution: "@grafana/i18n@workspace:packages/grafana-i18n" dependencies: @@ -3309,12 +3309,12 @@ __metadata: resolution: "@grafana/o11y-ds-frontend@workspace:packages/grafana-o11y-ds-frontend" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:12.2.0-pre" - "@grafana/e2e-selectors": "npm:12.2.0-pre" + "@grafana/data": "npm:12.3.0-pre" + "@grafana/e2e-selectors": "npm:12.3.0-pre" "@grafana/plugin-ui": "npm:^0.10.10" - "@grafana/runtime": "npm:12.2.0-pre" - "@grafana/schema": "npm:12.2.0-pre" - "@grafana/ui": "npm:12.2.0-pre" + "@grafana/runtime": "npm:12.3.0-pre" + "@grafana/schema": "npm:12.3.0-pre" + "@grafana/ui": "npm:12.3.0-pre" "@testing-library/dom": "npm:10.4.1" "@testing-library/jest-dom": "npm:^6.1.2" "@testing-library/react": "npm:16.3.0" @@ -3338,7 +3338,7 @@ __metadata: languageName: unknown linkType: soft -"@grafana/plugin-configs@npm:12.2.0-pre, @grafana/plugin-configs@workspace:*, @grafana/plugin-configs@workspace:packages/grafana-plugin-configs": +"@grafana/plugin-configs@npm:12.3.0-pre, @grafana/plugin-configs@workspace:*, @grafana/plugin-configs@workspace:packages/grafana-plugin-configs": version: 0.0.0-use.local resolution: "@grafana/plugin-configs@workspace:packages/grafana-plugin-configs" dependencies: @@ -3415,13 +3415,13 @@ __metadata: dependencies: "@emotion/css": "npm:11.13.5" "@floating-ui/react": "npm:0.27.16" - "@grafana/data": "npm:12.2.0-pre" - "@grafana/e2e-selectors": "npm:12.2.0-pre" - "@grafana/i18n": "npm:12.2.0-pre" + "@grafana/data": "npm:12.3.0-pre" + "@grafana/e2e-selectors": "npm:12.3.0-pre" + "@grafana/i18n": "npm:12.3.0-pre" "@grafana/plugin-ui": "npm:^0.10.10" - "@grafana/runtime": "npm:12.2.0-pre" - "@grafana/schema": "npm:12.2.0-pre" - "@grafana/ui": "npm:12.2.0-pre" + "@grafana/runtime": "npm:12.3.0-pre" + "@grafana/schema": "npm:12.3.0-pre" + "@grafana/ui": "npm:12.3.0-pre" "@hello-pangea/dnd": "npm:18.0.1" "@leeoniya/ufuzzy": "npm:1.0.18" "@lezer/common": "npm:1.2.3" @@ -3480,15 +3480,15 @@ __metadata: languageName: unknown linkType: soft -"@grafana/runtime@npm:12.2.0-pre, @grafana/runtime@workspace:*, @grafana/runtime@workspace:packages/grafana-runtime": +"@grafana/runtime@npm:12.3.0-pre, @grafana/runtime@workspace:*, @grafana/runtime@workspace:packages/grafana-runtime": version: 0.0.0-use.local resolution: "@grafana/runtime@workspace:packages/grafana-runtime" dependencies: - "@grafana/data": "npm:12.2.0-pre" - "@grafana/e2e-selectors": "npm:12.2.0-pre" + "@grafana/data": "npm:12.3.0-pre" + "@grafana/e2e-selectors": "npm:12.3.0-pre" "@grafana/faro-web-sdk": "npm:^1.13.2" - "@grafana/schema": "npm:12.2.0-pre" - "@grafana/ui": "npm:12.2.0-pre" + "@grafana/schema": "npm:12.3.0-pre" + "@grafana/ui": "npm:12.3.0-pre" "@rollup/plugin-node-resolve": "npm:16.0.1" "@rollup/plugin-terser": "npm:0.4.4" "@testing-library/dom": "npm:10.4.1" @@ -3567,7 +3567,7 @@ __metadata: languageName: node linkType: hard -"@grafana/schema@npm:12.2.0-pre, @grafana/schema@workspace:*, @grafana/schema@workspace:packages/grafana-schema": +"@grafana/schema@npm:12.3.0-pre, @grafana/schema@workspace:*, @grafana/schema@workspace:packages/grafana-schema": version: 0.0.0-use.local resolution: "@grafana/schema@workspace:packages/grafana-schema" dependencies: @@ -3583,17 +3583,17 @@ __metadata: languageName: unknown linkType: soft -"@grafana/sql@npm:12.2.0-pre, @grafana/sql@workspace:*, @grafana/sql@workspace:packages/grafana-sql": +"@grafana/sql@npm:12.3.0-pre, @grafana/sql@workspace:*, @grafana/sql@workspace:packages/grafana-sql": version: 0.0.0-use.local resolution: "@grafana/sql@workspace:packages/grafana-sql" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:12.2.0-pre" - "@grafana/e2e-selectors": "npm:12.2.0-pre" - "@grafana/i18n": "npm:12.2.0-pre" + "@grafana/data": "npm:12.3.0-pre" + "@grafana/e2e-selectors": "npm:12.3.0-pre" + "@grafana/i18n": "npm:12.3.0-pre" "@grafana/plugin-ui": "npm:^0.10.10" - "@grafana/runtime": "npm:12.2.0-pre" - "@grafana/ui": "npm:12.2.0-pre" + "@grafana/runtime": "npm:12.3.0-pre" + "@grafana/ui": "npm:12.3.0-pre" "@react-awesome-query-builder/ui": "npm:6.6.15" "@testing-library/dom": "npm:10.4.1" "@testing-library/jest-dom": "npm:^6.1.2" @@ -3649,7 +3649,7 @@ __metadata: languageName: node linkType: hard -"@grafana/ui@npm:12.2.0-pre, @grafana/ui@workspace:*, @grafana/ui@workspace:packages/grafana-ui": +"@grafana/ui@npm:12.3.0-pre, @grafana/ui@workspace:*, @grafana/ui@workspace:packages/grafana-ui": version: 0.0.0-use.local resolution: "@grafana/ui@workspace:packages/grafana-ui" dependencies: @@ -3659,11 +3659,11 @@ __metadata: "@emotion/serialize": "npm:1.3.3" "@faker-js/faker": "npm:^9.0.0" "@floating-ui/react": "npm:0.27.16" - "@grafana/data": "npm:12.2.0-pre" - "@grafana/e2e-selectors": "npm:12.2.0-pre" + "@grafana/data": "npm:12.3.0-pre" + "@grafana/e2e-selectors": "npm:12.3.0-pre" "@grafana/faro-web-sdk": "npm:^1.13.2" - "@grafana/i18n": "npm:12.2.0-pre" - "@grafana/schema": "npm:12.2.0-pre" + "@grafana/i18n": "npm:12.3.0-pre" + "@grafana/schema": "npm:12.3.0-pre" "@hello-pangea/dnd": "npm:18.0.1" "@monaco-editor/react": "npm:4.7.0" "@popperjs/core": "npm:2.11.8" From 3e086d11336b4f994f668be72c335ebd18f27e0d Mon Sep 17 00:00:00 2001 From: "alerting-team[bot]" <158350966+alerting-team[bot]@users.noreply.github.com> Date: Fri, 12 Sep 2025 15:01:13 -0400 Subject: [PATCH 05/33] Alerting: Update alerting module to f2728ab090eed9c6b70057b53239fb370d68e8ed (#111018) [create-pull-request] automated change Co-authored-by: santihernandezc <41638679+santihernandezc@users.noreply.github.com> Co-authored-by: Yuri Tseretyan --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 7165cace114..ec8cc6b2e9c 100644 --- a/go.mod +++ b/go.mod @@ -86,7 +86,7 @@ require ( github.com/googleapis/gax-go/v2 v2.14.2 // @grafana/grafana-backend-group github.com/gorilla/mux v1.8.1 // @grafana/grafana-backend-group github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // @grafana/grafana-app-platform-squad - github.com/grafana/alerting v0.0.0-20250911172908-2b26ef8f17eb // @grafana/alerting-backend + github.com/grafana/alerting v0.0.0-20250912123435-f2728ab090ee // @grafana/alerting-backend github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43 // @grafana/identity-access-team github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43 // @grafana/identity-access-team github.com/grafana/dataplane/examples v0.0.1 // @grafana/observability-metrics diff --git a/go.sum b/go.sum index ab196b94b3c..aa9d392df3b 100644 --- a/go.sum +++ b/go.sum @@ -1590,8 +1590,8 @@ github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7Fsg github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= -github.com/grafana/alerting v0.0.0-20250911172908-2b26ef8f17eb h1:g/gbEJoncYghiojMM6OwWJi1P+SC/mnjBG+E422p48o= -github.com/grafana/alerting v0.0.0-20250911172908-2b26ef8f17eb/go.mod h1:XWqj/rlsy4OV/E9XNNyFn+a7U4GNsSugPb2rDBj9+58= +github.com/grafana/alerting v0.0.0-20250912123435-f2728ab090ee h1:J/9l2w3Q5JDBEB3t5bDsxhxWldGtFd5KpYGoQi0m/hc= +github.com/grafana/alerting v0.0.0-20250912123435-f2728ab090ee/go.mod h1:XWqj/rlsy4OV/E9XNNyFn+a7U4GNsSugPb2rDBj9+58= github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43 h1:vVPT0i5Y1vI6qzecYStV2yk7cHKrC3Pc7AgvwT5KydQ= github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43/go.mod h1:1fWkOiL+m32NBgRHZtlZGz2ji868tPZACYbqP3nBRJI= github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43 h1:NlkGMnVi/oUn6Cr90QbJYpQJ4FnjyAIG9Ex5GtTZIzw= From c52eedbf23be6921152ce95d59b9986c6c840fb5 Mon Sep 17 00:00:00 2001 From: "lean.dev" <34773040+leandro-deveikis@users.noreply.github.com> Date: Fri, 12 Sep 2025 20:46:11 +0100 Subject: [PATCH 06/33] CloudMigration: fix flacky test (#111046) --- .../cloudmigrationimpl/cloudmigration_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/services/cloudmigration/cloudmigrationimpl/cloudmigration_test.go b/pkg/services/cloudmigration/cloudmigrationimpl/cloudmigration_test.go index 6b1e89f3096..948c63ea613 100644 --- a/pkg/services/cloudmigration/cloudmigrationimpl/cloudmigration_test.go +++ b/pkg/services/cloudmigration/cloudmigrationimpl/cloudmigration_test.go @@ -176,7 +176,7 @@ func Test_GetSnapshotStatusFromGMS(t *testing.T) { }) require.NoError(t, err) require.Eventually(t, checkStatusSync(ctx, s, snapshotUID, sessionUID, cloudmigration.SnapshotStatusPendingProcessing), time.Second, 10*time.Millisecond) - require.Equal(t, 1, gmsClientFake.GetSnapshotStatusCallCount()) + require.True(t, gmsClientFake.GetSnapshotStatusCallCount() >= 1) }) t.Run("test case: gms snapshot processing", func(t *testing.T) { @@ -200,7 +200,7 @@ func Test_GetSnapshotStatusFromGMS(t *testing.T) { }) require.NoError(t, err) require.Eventually(t, checkStatusSync(ctx, s, snapshotUID, sessionUID, cloudmigration.SnapshotStatusProcessing), time.Second, 10*time.Millisecond) - require.Equal(t, 1, gmsClientFake.GetSnapshotStatusCallCount()) + require.True(t, gmsClientFake.GetSnapshotStatusCallCount() >= 1) }) t.Run("test case: gms snapshot finished", func(t *testing.T) { @@ -224,7 +224,7 @@ func Test_GetSnapshotStatusFromGMS(t *testing.T) { }) require.NoError(t, err) require.Eventually(t, checkStatusSync(ctx, s, snapshotUID, sessionUID, cloudmigration.SnapshotStatusFinished), time.Second, 10*time.Millisecond) - require.Equal(t, 1, gmsClientFake.GetSnapshotStatusCallCount()) + require.True(t, gmsClientFake.GetSnapshotStatusCallCount() >= 1) }) t.Run("test case: gms snapshot canceled", func(t *testing.T) { @@ -248,7 +248,7 @@ func Test_GetSnapshotStatusFromGMS(t *testing.T) { }) require.NoError(t, err) require.Eventually(t, checkStatusSync(ctx, s, snapshotUID, sessionUID, cloudmigration.SnapshotStatusCanceled), time.Second, 10*time.Millisecond) - require.Equal(t, 1, gmsClientFake.GetSnapshotStatusCallCount()) + require.True(t, gmsClientFake.GetSnapshotStatusCallCount() >= 1) }) t.Run("test case: gms snapshot error", func(t *testing.T) { @@ -272,7 +272,7 @@ func Test_GetSnapshotStatusFromGMS(t *testing.T) { }) require.NoError(t, err) require.Eventually(t, checkStatusSync(ctx, s, snapshotUID, sessionUID, cloudmigration.SnapshotStatusError), time.Second, 10*time.Millisecond) - assert.Equal(t, 1, gmsClientFake.GetSnapshotStatusCallCount()) + assert.True(t, gmsClientFake.GetSnapshotStatusCallCount() >= 1) }) t.Run("test case: gms snapshot unknown", func(t *testing.T) { From c5ed2780abb8cf2c38ec67f1962876395ea96c9a Mon Sep 17 00:00:00 2001 From: Stephanie Hingtgen Date: Fri, 12 Sep 2025 14:02:13 -0600 Subject: [PATCH 07/33] Provisioning: Fix deletion order (#111043) --- .../provisioning/controller/finalizers.go | 36 ++++++++-- .../controller/finalizers_test.go | 66 +++++++++++++++++++ 2 files changed, 95 insertions(+), 7 deletions(-) create mode 100644 pkg/registry/apis/provisioning/controller/finalizers_test.go diff --git a/pkg/registry/apis/provisioning/controller/finalizers.go b/pkg/registry/apis/provisioning/controller/finalizers.go index 3980db5ec6f..72c2209911c 100644 --- a/pkg/registry/apis/provisioning/controller/finalizers.go +++ b/pkg/registry/apis/provisioning/controller/finalizers.go @@ -160,14 +160,36 @@ func sortResourceListForDeletion(list *provisioning.ResourceList) { // Sort by the following logic: // - Put folders at the end so that we empty them first. // - Sort folders by depth so that we remove the deepest first + // - If the repo is created within a folder in grafana, make sure that folder is last. sort.Slice(list.Items, func(i, j int) bool { - switch { - case list.Items[i].Group != folders.RESOURCE: - return true - case list.Items[j].Group != folders.RESOURCE: - return false - default: - return len(strings.Split(list.Items[i].Path, "/")) > len(strings.Split(list.Items[j].Path, "/")) + isFolderI := list.Items[i].Group == folders.GroupVersion.Group + isFolderJ := list.Items[j].Group == folders.GroupVersion.Group + + // non-folders always go first in the order of deletion. + if isFolderI != isFolderJ { + return !isFolderI } + + // if both are not folders, keep order (doesn't matter) + if !isFolderI && !isFolderJ { + return false + } + + hasFolderI := list.Items[i].Folder != "" + hasFolderJ := list.Items[j].Folder != "" + // if one folder is in the root (i.e. does not have a folder specified), put that last + if hasFolderI != hasFolderJ { + return hasFolderI + } + + // if both are nested folder, sort by depth, with the deepest one being first + depthI := len(strings.Split(list.Items[i].Path, "/")) + depthJ := len(strings.Split(list.Items[j].Path, "/")) + if depthI != depthJ { + return depthI > depthJ + } + + // otherwise, keep order (doesn't matter) + return false }) } diff --git a/pkg/registry/apis/provisioning/controller/finalizers_test.go b/pkg/registry/apis/provisioning/controller/finalizers_test.go new file mode 100644 index 00000000000..a44e595afbf --- /dev/null +++ b/pkg/registry/apis/provisioning/controller/finalizers_test.go @@ -0,0 +1,66 @@ +package controller + +import ( + "testing" + + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" + "github.com/stretchr/testify/assert" +) + +func TestSortResourceListForDeletion(t *testing.T) { + testCases := []struct { + name string + input provisioning.ResourceList + expected provisioning.ResourceList + }{ + { + name: "Non-folder items first, folders sorted by depth", + input: provisioning.ResourceList{ + Items: []provisioning.ResourceListItem{ + {Group: "dashboard.grafana.app", Path: "dashboard1.json"}, + {Group: "folder.grafana.app", Path: "folder1"}, + {Group: "folder.grafana.app", Path: "folder1/subfolder1/subfolder2", Folder: "subfolder1"}, + {Group: "dashboard.grafana.app", Path: "dashboard2.json"}, + {Group: "folder.grafana.app", Path: "folder2"}, + {Group: "folder.grafana.app", Path: "folder1/subfolder1", Folder: "folder1"}, + }, + }, + expected: provisioning.ResourceList{ + Items: []provisioning.ResourceListItem{ + {Group: "dashboard.grafana.app", Path: "dashboard1.json"}, + {Group: "dashboard.grafana.app", Path: "dashboard2.json"}, + {Group: "folder.grafana.app", Path: "folder1/subfolder1/subfolder2", Folder: "subfolder1"}, + {Group: "folder.grafana.app", Path: "folder1/subfolder1", Folder: "folder1"}, + {Group: "folder.grafana.app", Path: "folder1"}, + {Group: "folder.grafana.app", Path: "folder2"}, + }, + }, + }, + { + name: "Folders without parent should be last", + input: provisioning.ResourceList{ + Items: []provisioning.ResourceListItem{ + {Group: "folder.grafana.app", Path: "folder1"}, + {Group: "folder.grafana.app", Path: "folder2", Folder: "folder1"}, // if a repo is created with a folder in grafana (here folder1), the path will not have /, but the folder will be set + {Group: "folder.grafana.app", Path: "folder2/subfolder1", Folder: "folder2"}, + {Group: "folder.grafana.app", Path: "folder3", Folder: "folder1"}, + }, + }, + expected: provisioning.ResourceList{ + Items: []provisioning.ResourceListItem{ + {Group: "folder.grafana.app", Path: "folder2/subfolder1", Folder: "folder2"}, + {Group: "folder.grafana.app", Path: "folder2", Folder: "folder1"}, + {Group: "folder.grafana.app", Path: "folder3", Folder: "folder1"}, + {Group: "folder.grafana.app", Path: "folder1"}, + }, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + sortResourceListForDeletion(&tc.input) + assert.Equal(t, tc.expected, tc.input) + }) + } +} From 7ce971cba116c7ec8f4c8290bd48b969a135f4df Mon Sep 17 00:00:00 2001 From: owensmallwood Date: Fri, 12 Sep 2025 14:40:16 -0600 Subject: [PATCH 08/33] Unified Storage: Adds pruner for kv eventstore (#110785) * Adds pruner for eventstore - default 24 hours. Adds tests. * update comment * remove delay on startup. formatting * updates log message type and removes useless comment * caller handles goroutine for runCleanupOldEvents() * simplify timestamp extraction * adds config for event pruning interval * uses start and end key to get all expired events * remove sort when listing keys in event pruner - order doesnt matter * use snowflake constants * log when we delete 0 rows * pass time.Time to cleanup old events func --- pkg/storage/unified/resource/eventstore.go | 30 ++++ .../unified/resource/eventstore_test.go | 133 ++++++++++++++++++ .../unified/resource/storage_backend.go | 102 ++++++++++---- 3 files changed, 241 insertions(+), 24 deletions(-) diff --git a/pkg/storage/unified/resource/eventstore.go b/pkg/storage/unified/resource/eventstore.go index f2a3bc4e028..651fcb52092 100644 --- a/pkg/storage/unified/resource/eventstore.go +++ b/pkg/storage/unified/resource/eventstore.go @@ -7,6 +7,9 @@ import ( "iter" "strconv" "strings" + "time" + + "github.com/bwmarrin/snowflake" ) const ( @@ -224,3 +227,30 @@ func (n *eventStore) ListSince(ctx context.Context, sinceRV int64) iter.Seq2[Eve } } } + +// CleanupOldEvents deletes events older than the specified retention period. +func (n *eventStore) CleanupOldEvents(ctx context.Context, cutoff time.Time) (int, error) { + deletedCount := 0 + + // Keys are stored in the format of "resource_version~namespace~group~resource~name" + // With a start key of "1" and an end key of the cutoff time we can get all expired events. + endKey := fmt.Sprintf("%d", snowflakeFromTime(cutoff)) + for key, err := range n.kv.Keys(ctx, eventsSection, ListOptions{StartKey: "1", EndKey: endKey}) { + if err != nil { + return deletedCount, fmt.Errorf("failed to list event keys: %w", err) + } + + // TODO should use batch deletes here when available + if err := n.kv.Delete(ctx, eventsSection, key); err != nil { + return deletedCount, fmt.Errorf("failed to delete event key %s: %w", key, err) + } + deletedCount++ + } + + return deletedCount, nil +} + +// snowflake id with last two sections set to 0 (machine id and sequence) +func snowflakeFromTime(t time.Time) int64 { + return (t.UnixMilli() - snowflake.Epoch) << (snowflake.NodeBits + snowflake.StepBits) +} diff --git a/pkg/storage/unified/resource/eventstore_test.go b/pkg/storage/unified/resource/eventstore_test.go index 7782e879094..63ddc999567 100644 --- a/pkg/storage/unified/resource/eventstore_test.go +++ b/pkg/storage/unified/resource/eventstore_test.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -468,3 +469,135 @@ func TestEventStore_Save_InvalidJSON(t *testing.T) { err := store.Save(ctx, event) assert.NoError(t, err) } + +func TestEventStore_CleanupOldEvents(t *testing.T) { + ctx := context.Background() + store := setupTestEventStore(t) + + now := time.Now() + oldRV := snowflakeFromTime(now.Add(-48 * time.Hour)) // 48 hours ago + recentRV := snowflakeFromTime(now.Add(-1 * time.Hour)) // 1 hour ago + + oldEvent := Event{ + Namespace: "default", + Group: "apps", + Resource: "resource", + Name: "old-resource", + ResourceVersion: oldRV, + Action: DataActionCreated, + Folder: "test-folder", + PreviousRV: 999, + } + + recentEvent := Event{ + Namespace: "default", + Group: "apps", + Resource: "resource", + Name: "recent-resource", + ResourceVersion: recentRV, + Action: DataActionCreated, + Folder: "test-folder", + PreviousRV: 999, + } + + // Save both events + err := store.Save(ctx, oldEvent) + require.NoError(t, err) + err = store.Save(ctx, recentEvent) + require.NoError(t, err) + + // Verify both events exist + _, err = store.Get(ctx, EventKey{ + Namespace: oldEvent.Namespace, + Group: oldEvent.Group, + Resource: oldEvent.Resource, + Name: oldEvent.Name, + ResourceVersion: oldEvent.ResourceVersion, + Action: oldEvent.Action, + }) + require.NoError(t, err) + + _, err = store.Get(ctx, EventKey{ + Namespace: recentEvent.Namespace, + Group: recentEvent.Group, + Resource: recentEvent.Resource, + Name: recentEvent.Name, + ResourceVersion: recentEvent.ResourceVersion, + Action: recentEvent.Action, + }) + require.NoError(t, err) + + // Clean up events older than 24 hours + deletedCount, err := store.CleanupOldEvents(ctx, time.Now().Add(-24*time.Hour)) + require.NoError(t, err) + assert.Equal(t, 1, deletedCount, "Should have deleted 1 old event") + + // Verify old event was deleted + _, err = store.Get(ctx, EventKey{ + Namespace: oldEvent.Namespace, + Group: oldEvent.Group, + Resource: oldEvent.Resource, + Name: oldEvent.Name, + ResourceVersion: oldEvent.ResourceVersion, + Action: oldEvent.Action, + }) + assert.Error(t, err, "Old event should have been deleted") + + // Verify recent event still exists + _, err = store.Get(ctx, EventKey{ + Namespace: recentEvent.Namespace, + Group: recentEvent.Group, + Resource: recentEvent.Resource, + Name: recentEvent.Name, + ResourceVersion: recentEvent.ResourceVersion, + Action: recentEvent.Action, + }) + require.NoError(t, err, "Recent event should still exist") +} + +func TestEventStore_CleanupOldEvents_NoOldEvents(t *testing.T) { + ctx := context.Background() + store := setupTestEventStore(t) + + // Create an event 1 hour old + rv := snowflakeFromTime(time.Now().Add(-1 * time.Hour)) + event := Event{ + Namespace: "default", + Group: "apps", + Resource: "resource", + Name: "recent-resource", + ResourceVersion: rv, + Action: DataActionCreated, + Folder: "test-folder", + PreviousRV: 999, + } + + err := store.Save(ctx, event) + require.NoError(t, err) + + // Clean up events older than 24 hours + deletedCount, err := store.CleanupOldEvents(ctx, time.Now().Add(-24*time.Hour)) + require.NoError(t, err) + assert.Equal(t, 0, deletedCount, "Should not have deleted any events") + + // Verify event still exists + _, err = store.Get(ctx, EventKey{ + Namespace: event.Namespace, + Group: event.Group, + Resource: event.Resource, + Name: event.Name, + ResourceVersion: event.ResourceVersion, + Action: event.Action, + }) + require.NoError(t, err, "Recent event should still exist") +} + +func TestEventStore_CleanupOldEvents_EmptyStore(t *testing.T) { + ctx := context.Background() + store := setupTestEventStore(t) + + // Clean up events from empty store + deletedCount, err := store.CleanupOldEvents(ctx, time.Now().Add(-24*time.Hour)) + require.NoError(t, err) + assert.Equal(t, 0, deletedCount, "Should not have deleted any events from empty store") +} diff --git a/pkg/storage/unified/resource/storage_backend.go b/pkg/storage/unified/resource/storage_backend.go index 4f5490f7c7d..54bd33ba67f 100644 --- a/pkg/storage/unified/resource/storage_backend.go +++ b/pkg/storage/unified/resource/storage_backend.go @@ -25,22 +25,26 @@ import ( ) const ( - defaultListBufferSize = 100 - prunerMaxEvents = 20 + defaultListBufferSize = 100 + prunerMaxEvents = 20 + defaultEventRetentionPeriod = 1 * time.Hour + defaultEventPruningInterval = 5 * time.Minute ) // kvStorageBackend Unified storage backend based on KV storage. type kvStorageBackend struct { - snowflake *snowflake.Node - kv KV - dataStore *dataStore - metaStore *metadataStore - eventStore *eventStore - notifier *notifier - builder DocumentBuilder - log logging.Logger - withPruner bool - historyPruner Pruner + snowflake *snowflake.Node + kv KV + dataStore *dataStore + metaStore *metadataStore + eventStore *eventStore + notifier *notifier + builder DocumentBuilder + log logging.Logger + withPruner bool + eventRetentionPeriod time.Duration + eventPruningInterval time.Duration + historyPruner Pruner //tracer trace.Tracer //reg prometheus.Registerer } @@ -48,10 +52,12 @@ type kvStorageBackend struct { var _ StorageBackend = &kvStorageBackend{} type KvBackendOptions struct { - KvStore KV - WithPruner bool - Tracer trace.Tracer // TODO add tracing - Reg prometheus.Registerer // TODO add metrics + KvStore KV + WithPruner bool + EventRetentionPeriod time.Duration // How long to keep events (default: 1 hour) + EventPruningInterval time.Duration // How often to run the event pruning (default: 5 minutes) + Tracer trace.Tracer // TODO add tracing + Reg prometheus.Registerer // TODO add metrics } func NewKvStorageBackend(opts KvBackendOptions) (StorageBackend, error) { @@ -63,23 +69,71 @@ func NewKvStorageBackend(opts KvBackendOptions) (StorageBackend, error) { return nil, fmt.Errorf("failed to create snowflake node: %w", err) } eventStore := newEventStore(kv) + + eventRetentionPeriod := opts.EventRetentionPeriod + if eventRetentionPeriod <= 0 { + eventRetentionPeriod = defaultEventRetentionPeriod + } + + eventPruningInterval := opts.EventPruningInterval + if eventPruningInterval <= 0 { + eventPruningInterval = defaultEventPruningInterval + } + backend := &kvStorageBackend{ - kv: kv, - dataStore: newDataStore(kv), - metaStore: newMetadataStore(kv), - eventStore: eventStore, - notifier: newNotifier(eventStore, notifierOptions{}), - snowflake: s, - builder: StandardDocumentBuilder(), // For now we use the standard document builder. - log: &logging.NoOpLogger{}, // Make this configurable + kv: kv, + dataStore: newDataStore(kv), + metaStore: newMetadataStore(kv), + eventStore: eventStore, + notifier: newNotifier(eventStore, notifierOptions{}), + snowflake: s, + builder: StandardDocumentBuilder(), // For now we use the standard document builder. + log: &logging.NoOpLogger{}, // Make this configurable + eventRetentionPeriod: eventRetentionPeriod, + eventPruningInterval: eventPruningInterval, } err = backend.initPruner(ctx) if err != nil { return nil, fmt.Errorf("failed to initialize pruner: %w", err) } + + // Start the event cleanup background job + go backend.runCleanupOldEvents(ctx) + return backend, nil } +// runCleanupOldEvents starts a background goroutine that periodically cleans up old events +func (k *kvStorageBackend) runCleanupOldEvents(ctx context.Context) { + // Run cleanup every hour + ticker := time.NewTicker(k.eventPruningInterval) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + k.log.Debug("Event cleanup stopped due to context cancellation") + return + case <-ticker.C: + k.cleanupOldEvents(ctx) + } + } +} + +// cleanupOldEvents performs the actual cleanup of old events +func (k *kvStorageBackend) cleanupOldEvents(ctx context.Context) { + cutoff := time.Now().Add(-k.eventRetentionPeriod) + deletedCount, err := k.eventStore.CleanupOldEvents(ctx, cutoff) + if err != nil { + k.log.Error("Failed to cleanup old events", "error", err) + return + } + + if deletedCount == 0 { + k.log.Info("Cleaned up old events", "deleted_count", deletedCount, "retention_period", k.eventRetentionPeriod) + } +} + func (k *kvStorageBackend) pruneEvents(ctx context.Context, key PruningKey) error { if !key.Validate() { return fmt.Errorf("invalid pruning key, all fields must be set: %+v", key) From cb37539ed7ca14230fda6e61012ab519eabccab7 Mon Sep 17 00:00:00 2001 From: Paul Marbach Date: Fri, 12 Sep 2025 17:22:30 -0400 Subject: [PATCH 09/33] Table: Fix logic to calculate footer height (#110954) * Table: Fix logic to calculate footer height * add non-numeric footer case to gdev * Update packages/grafana-ui/src/components/Table/TableNG/utils.ts Co-authored-by: Leon Sorokin * Update packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx Co-authored-by: Leon Sorokin --------- Co-authored-by: Leon Sorokin --- .../panel-table/table_footer.json | 61 +++++++++++++++++++ .../src/components/Table/TableNG/TableNG.tsx | 18 +++--- .../components/Table/TableNG/utils.test.ts | 30 +++++++++ .../src/components/Table/TableNG/utils.ts | 52 +++------------- 4 files changed, 108 insertions(+), 53 deletions(-) diff --git a/devenv/dev-dashboards/panel-table/table_footer.json b/devenv/dev-dashboards/panel-table/table_footer.json index fddbeaa7747..6d7ca427f7b 100644 --- a/devenv/dev-dashboards/panel-table/table_footer.json +++ b/devenv/dev-dashboards/panel-table/table_footer.json @@ -1442,6 +1442,67 @@ } ], "type": "table" + }, + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "footer": { + "reducers": ["lastNotNull", "countAll"] + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 12, + "x": 0, + "y": 24 + }, + "id": 9, + "options": { + "cellHeight": "sm", + "showHeader": true + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "csvContent": "a,b\nfoo,bar\nbaz,bim\nbop,boop", + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "No numeric fields", + "type": "table" } ], "preload": false, diff --git a/packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx b/packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx index 5b5fcc5b2a4..e21e3c59be9 100644 --- a/packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx +++ b/packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx @@ -108,7 +108,6 @@ export function TableNG(props: TableNGProps) { enablePagination = false, enableSharedCrosshair = false, enableVirtualization, - fieldConfig, frozenColumns = 0, getActions = () => [], height, @@ -125,12 +124,6 @@ export function TableNG(props: TableNGProps) { width, } = props; - const hasFooter = useMemo( - () => data.fields.some((field) => field.config?.custom?.footer?.reducers?.length ?? false), - [data.fields] - ); - const footerHeight = hasFooter ? calculateFooterHeight(data, fieldConfig) : 0; - const theme = useTheme2(); const styles = useStyles2(getGridStyles, enablePagination, transparent); const panelContext = usePanelContext(); @@ -146,7 +139,16 @@ export function TableNG(props: TableNGProps) { [getActions, data, userCanExecuteActions] ); + const visibleFields = useMemo(() => getVisibleFields(data.fields), [data.fields]); const hasHeader = !noHeader; + const hasFooter = useMemo( + () => visibleFields.some((field) => Boolean(field.config.custom?.footer?.reducers?.length)), + [visibleFields] + ); + const footerHeight = useMemo( + () => (hasFooter ? calculateFooterHeight(visibleFields) : 0), + [hasFooter, visibleFields] + ); const resizeHandler = useColumnResize(onColumnResize); @@ -173,7 +175,7 @@ export function TableNG(props: TableNGProps) { const [expandedRows, setExpandedRows] = useState(() => new Set()); // vt scrollbar accounting for column auto-sizing - const visibleFields = useMemo(() => getVisibleFields(data.fields), [data.fields]); + const defaultRowHeight = useMemo( () => getDefaultRowHeight(theme, visibleFields, cellHeight), [theme, visibleFields, cellHeight] diff --git a/packages/grafana-ui/src/components/Table/TableNG/utils.test.ts b/packages/grafana-ui/src/components/Table/TableNG/utils.test.ts index 7331c048a92..946ee3fd799 100644 --- a/packages/grafana-ui/src/components/Table/TableNG/utils.test.ts +++ b/packages/grafana-ui/src/components/Table/TableNG/utils.test.ts @@ -46,6 +46,7 @@ import { getDefaultRowHeight, getDisplayName, predicateByName, + calculateFooterHeight, } from './utils'; describe('TableNG utils', () => { @@ -1380,6 +1381,35 @@ describe('TableNG utils', () => { }); }); + describe('calculateFooterHeight', () => { + it('should return 0 if no footer is present', () => { + const frame = createDataFrame({ + fields: [ + { name: 'time', values: [1, 1, 2], nanos: [100, 99, 0] }, + { name: 'value', values: [10, 20, 30] }, + ], + }); + + expect(calculateFooterHeight(frame.fields)).toBe(0); + }); + + it('should return the height in pixels for the max reducers on a given field', () => { + const frame = createDataFrame({ + fields: [ + { + name: 'time', + values: [1, 1, 2], + nanos: [100, 99, 0], + config: { custom: { footer: { reducers: ['min', 'max', 'count'] } } }, + }, + { name: 'value', values: [10, 20, 30], config: { custom: { footer: { reducers: ['min'] } } } }, + ], + }); + + expect(calculateFooterHeight(frame.fields)).toBe(78); // 3 reducers * 22px line height + 12px padding + }); + }); + describe('getDisplayName', () => { it('should return the display name if set', () => { const field: Field = { diff --git a/packages/grafana-ui/src/components/Table/TableNG/utils.ts b/packages/grafana-ui/src/components/Table/TableNG/utils.ts index 7da729539a3..162c8e6df91 100644 --- a/packages/grafana-ui/src/components/Table/TableNG/utils.ts +++ b/packages/grafana-ui/src/components/Table/TableNG/utils.ts @@ -8,7 +8,6 @@ import { Count, varPreLine } from 'uwrap'; import { FieldType, Field, - FieldConfigSource, formattedValueToString, GrafanaTheme2, DisplayValue, @@ -842,55 +841,18 @@ export const processNestedTableRows = ( return result; }; -/** - * @internal - * Get the maximum number of reducers across all fields - */ -const getMaxReducerCount = (dataFrame: DataFrame, fieldConfig?: FieldConfigSource): number => { - // Filter to only numeric fields that can have reducers - const numericFields = dataFrame.fields.filter(({ type }) => type === FieldType.number); - - // If there are no numeric fields, return 0 - if (numericFields.length === 0) { - return 0; - } - - // Map each field to its reducer count (direct config or override) - const reducerCounts = numericFields.map((field) => { - // Get the direct reducer count from the field config - const directReducers = field.config?.custom?.footer?.reducers ?? []; - let reducerCount = directReducers.length; - - // Check for overrides if field config is available - if (fieldConfig?.overrides) { - // Find override that matches this field - const override = fieldConfig.overrides.find( - ({ matcher: { id, options } }) => id === 'byName' && options === getDisplayName(field) - ); - - // Check if there's a footer reducer property in the override - const footerProperty = override?.properties?.find(({ id }) => id === 'custom.footer.reducers'); - if (footerProperty?.value && Array.isArray(footerProperty.value)) { - // If override exists, it takes precedence over direct config - reducerCount = footerProperty.value.length; - } - } - - return reducerCount; - }); - - // Return the maximum count or 0 if no reducers found - return reducerCounts.length > 0 ? Math.max(...reducerCounts) : 0; -}; - /** * @internal * Calculate the footer height based on the maximum reducer count */ -export const calculateFooterHeight = (dataFrame: DataFrame, fieldConfig?: FieldConfigSource) => { - const maxReducerCount = getMaxReducerCount(dataFrame, fieldConfig); +export const calculateFooterHeight = (fields: Field[]): number => { + let maxReducerCount = 0; + for (const field of fields) { + maxReducerCount = Math.max(maxReducerCount, field.config.custom?.footer?.reducers?.length ?? 0); + } + // Base height (+ padding) + height per reducer - return maxReducerCount * TABLE.LINE_HEIGHT + TABLE.CELL_PADDING * 2; + return maxReducerCount > 0 ? maxReducerCount * TABLE.LINE_HEIGHT + TABLE.CELL_PADDING * 2 : 0; }; /** From f258d8a41726e7bb482ae1cf8549a2686bfdb38c Mon Sep 17 00:00:00 2001 From: Paul Marbach Date: Fri, 12 Sep 2025 17:33:06 -0400 Subject: [PATCH 10/33] Table: Restore previous footer behavior of reducers applying to filtered data (#111041) * Table: Restore previous footer behavior of reducers applying to filtered data * update e2e to match new behavior --- e2e-playwright/panels-suite/table-footer.spec.ts | 4 ++-- packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/e2e-playwright/panels-suite/table-footer.spec.ts b/e2e-playwright/panels-suite/table-footer.spec.ts index 4c7064fa1fc..aecd1df29a3 100644 --- a/e2e-playwright/panels-suite/table-footer.spec.ts +++ b/e2e-playwright/panels-suite/table-footer.spec.ts @@ -11,7 +11,7 @@ const waitForTableLoad = async (loc: Page | Locator) => { }; test.describe('Panels test: Table - Footer', { tag: ['@panels', '@table'] }, () => { - test('Footer unaffected by filtering', async ({ gotoDashboardPage, selectors, page }) => { + test('Footer affected by filtering', async ({ gotoDashboardPage, selectors, page }) => { const dashboardPage = await gotoDashboardPage({ uid: DASHBOARD_UID, queryParams: new URLSearchParams({ editPanel: '4' }), @@ -51,7 +51,7 @@ test.describe('Panels test: Table - Footer', { tag: ['@panels', '@table'] }, () dashboardPage .getByGrafanaSelector(selectors.components.Panels.Visualization.TableNG.Footer.Value) .nth(minColumnIdx) - ).toHaveText(minReducerValue); + ).not.toHaveText(minReducerValue); }); test('Footer unaffected by sorting', async ({ gotoDashboardPage, selectors, page }) => { diff --git a/packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx b/packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx index e21e3c59be9..726a9a3ccf0 100644 --- a/packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx +++ b/packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx @@ -679,7 +679,7 @@ export function TableNG(props: TableNGProps) { ), renderSummaryCell: () => ( Date: Fri, 12 Sep 2025 23:35:10 +0200 Subject: [PATCH 11/33] Graphite: Backend metrics expand endpoint (#110678) * Add lint rules * Backend decoupling - Add standalone files - Add graphite query type - Add logger to Service - Create logger in the ProvideService method - Use a pointer for the HTTP client provider - Update logger usage everywhere - Update tracer type - Replace simplejson with json - Add dummy CallResource and CheckHealth methods - Update tests * Update ConfigEditor imports * Update types imports * Update datasource - Switch to using semver package - Update imports * Update store imports * Update helper imports and notification creation * Update context import * Update version numbers and logic * Copy array_move from core * Test updates * Add required files and update plugin.json * Update core references and packages * Remove commented code * Update wire * Lint * Fix import * Copy null type * More lint * Update snapshot * Refactor backend - Split query logic into separate file - Move utils to separate file * Add health-check logic - Support backend healthcheck if the FF is enabled * Remove query import support as unneeded * Add test * Add util function for decoding responses * Add events types * Add resource handler * Add events handler and generic resource req handler * Tests * Update frontend - Add types - Update events function to support backend requests * Lint and typing * Lint * Add metrics find endpoint - Add types - Add generic response parser - Add endpoint - Tests * Update FE functoin to use backend endpoint * Lint * Simplify request * Update test * Metrics expand type * Extract shared logic and add metric expand endpoint * Update tests * Call metric expand from backend * Add tests * Review * Review * Fix packages * Format * Fix merge issues * Review * Fix undefined values * Extract request creation - Add method for create requests generically with tests - Replace usage in query method - Update usages in resource handlers - Update tests - Update types --- pkg/tsdb/graphite/graphite.go | 38 ++ pkg/tsdb/graphite/graphite_test.go | 241 +++++++ pkg/tsdb/graphite/query.go | 25 +- pkg/tsdb/graphite/resource_handler.go | 227 ++++--- pkg/tsdb/graphite/resource_handler_test.go | 605 +++++++++++++++--- pkg/tsdb/graphite/types.go | 14 + .../plugins/datasource/graphite/datasource.ts | 18 +- 7 files changed, 959 insertions(+), 209 deletions(-) create mode 100644 pkg/tsdb/graphite/graphite_test.go diff --git a/pkg/tsdb/graphite/graphite.go b/pkg/tsdb/graphite/graphite.go index 406ba10f106..62b932763af 100644 --- a/pkg/tsdb/graphite/graphite.go +++ b/pkg/tsdb/graphite/graphite.go @@ -4,6 +4,8 @@ import ( "context" "fmt" "net/http" + "net/url" + "path" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" @@ -94,3 +96,39 @@ func (s *Service) QueryData(ctx context.Context, req *backend.QueryDataRequest) func (s *Service) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error { return s.resourceHandler.CallResource(ctx, req, sender) } + +func (s *Service) createRequest(ctx context.Context, dsInfo *datasourceInfo, params URLParams) (*http.Request, error) { + u, err := url.Parse(dsInfo.URL) + if err != nil { + return nil, err + } + + if params.SubPath != "" { + u.Path = path.Join(u.Path, params.SubPath) + } + + if params.QueryParams != nil { + queryValues := u.Query() + for k, v := range params.QueryParams { + queryValues.Set(k, v) + } + u.RawQuery = queryValues.Encode() + } + + method := params.Method + if method == "" { + method = http.MethodGet + } + + req, err := http.NewRequestWithContext(ctx, method, u.String(), params.Body) + if err != nil { + s.logger.Info("Failed to create request", "error", err) + return nil, fmt.Errorf("failed to create request: %w", err) + } + + for k, v := range params.Headers { + req.Header.Add(k, v) + } + + return req, err +} diff --git a/pkg/tsdb/graphite/graphite_test.go b/pkg/tsdb/graphite/graphite_test.go new file mode 100644 index 00000000000..bff49068f30 --- /dev/null +++ b/pkg/tsdb/graphite/graphite_test.go @@ -0,0 +1,241 @@ +package graphite + +import ( + "context" + "io" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_CreateRequest(t *testing.T) { + ctx := context.Background() + + service := &Service{} + dsInfo := &datasourceInfo{ + URL: "http://graphite.example.com", + } + + tests := []struct { + name string + dsInfo *datasourceInfo + params URLParams + expectedURL string + expectedMethod string + expectedError string + checkHeaders map[string]string + checkQuery map[string]string + }{ + { + name: "basic request with default GET method", + dsInfo: dsInfo, + params: URLParams{}, + expectedURL: "http://graphite.example.com", + expectedMethod: "GET", + }, + { + name: "request with subpath", + dsInfo: dsInfo, + params: URLParams{ + SubPath: "/metrics/find", + }, + expectedURL: "http://graphite.example.com/metrics/find", + expectedMethod: "GET", + }, + { + name: "request with custom method", + dsInfo: dsInfo, + params: URLParams{ + Method: "POST", + }, + expectedURL: "http://graphite.example.com", + expectedMethod: "POST", + }, + { + name: "request with query parameters", + dsInfo: dsInfo, + params: URLParams{ + QueryParams: map[string]string{ + "query": "stats.counters.*", + "format": "json", + }, + }, + expectedURL: "http://graphite.example.com", + expectedMethod: "GET", + checkQuery: map[string]string{ + "query": "stats.counters.*", + "format": "json", + }, + }, + { + name: "request with headers", + dsInfo: dsInfo, + params: URLParams{ + Headers: map[string]string{ + "Content-Type": "application/json", + }, + }, + expectedURL: "http://graphite.example.com", + expectedMethod: "GET", + checkHeaders: map[string]string{ + "Content-Type": "application/json", + }, + }, + { + name: "request with body", + dsInfo: dsInfo, + params: URLParams{ + Method: "POST", + Body: strings.NewReader(`{"test": "data"}`), + }, + expectedURL: "http://graphite.example.com", + expectedMethod: "POST", + }, + { + name: "complex request with all parameters", + dsInfo: dsInfo, + params: URLParams{ + SubPath: "/metrics/expand", + Method: "POST", + QueryParams: map[string]string{ + "groupByExpr": "true", + "leavesOnly": "false", + }, + Headers: map[string]string{ + "X-Custom-Header": "test-value", + }, + Body: strings.NewReader(`{"query": "stats.*"}`), + }, + expectedURL: "http://graphite.example.com/metrics/expand", + expectedMethod: "POST", + checkQuery: map[string]string{ + "groupByExpr": "true", + "leavesOnly": "false", + }, + checkHeaders: map[string]string{ + "X-Custom-Header": "test-value", + }, + }, + { + name: "invalid URL in datasource", + dsInfo: &datasourceInfo{ + URL: "://invalid-url", + }, + params: URLParams{}, + expectedError: "missing protocol scheme", + }, + { + name: "empty query parameter values", + dsInfo: dsInfo, + params: URLParams{ + QueryParams: map[string]string{ + "empty": "", + "valid": "value", + }, + }, + expectedURL: "http://graphite.example.com", + expectedMethod: "GET", + checkQuery: map[string]string{ + "empty": "", + "valid": "value", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + req, err := service.createRequest(ctx, tt.dsInfo, tt.params) + + if tt.expectedError != "" { + require.Error(t, err) + assert.Contains(t, err.Error(), tt.expectedError) + return + } + + require.NoError(t, err) + require.NotNil(t, req) + + // Check URL (base URL without query parameters) + baseURL := req.URL.Scheme + "://" + req.URL.Host + req.URL.Path + assert.Equal(t, tt.expectedURL, baseURL) + assert.Equal(t, tt.expectedMethod, req.Method) + + if tt.checkQuery != nil { + for key, expectedValue := range tt.checkQuery { + actualValue := req.URL.Query().Get(key) + assert.Equal(t, expectedValue, actualValue, "Query parameter %s", key) + } + } + + if tt.checkHeaders != nil { + for key, expectedValue := range tt.checkHeaders { + actualValue := req.Header.Get(key) + assert.Equal(t, expectedValue, actualValue, "Header %s", key) + } + } + + if tt.params.Body != nil { + bodyBytes, err := io.ReadAll(req.Body) + require.NoError(t, err) + + expectedContent := "" + switch tt.name { + case "request with body": + expectedContent = `{"test": "data"}` + case "complex request with all parameters": + expectedContent = `{"query": "stats.*"}` + } + assert.Equal(t, expectedContent, string(bodyBytes)) + } + }) + } +} + +func Test_CreateRequest_Body(t *testing.T) { + ctx := context.Background() + service := &Service{} + dsInfo := &datasourceInfo{URL: "http://graphite.example.com"} + + t.Run("string reader body", func(t *testing.T) { + bodyContent := `{"query": "stats.*", "format": "json"}` + params := URLParams{ + Method: "POST", + Body: strings.NewReader(bodyContent), + } + + req, err := service.createRequest(ctx, dsInfo, params) + require.NoError(t, err) + + // Read the body to verify content + bodyBytes, err := io.ReadAll(req.Body) + require.NoError(t, err) + assert.Equal(t, bodyContent, string(bodyBytes)) + }) + + t.Run("nil body", func(t *testing.T) { + params := URLParams{ + Method: "GET", + Body: nil, + } + + req, err := service.createRequest(ctx, dsInfo, params) + require.NoError(t, err) + assert.Nil(t, req.Body) + }) + + t.Run("empty body reader", func(t *testing.T) { + params := URLParams{ + Method: "POST", + Body: strings.NewReader(""), + } + + req, err := service.createRequest(ctx, dsInfo, params) + require.NoError(t, err) + + bodyBytes, err := io.ReadAll(req.Body) + require.NoError(t, err) + assert.Empty(t, string(bodyBytes)) + }) +} diff --git a/pkg/tsdb/graphite/query.go b/pkg/tsdb/graphite/query.go index 4889328aeee..331dbc1af34 100644 --- a/pkg/tsdb/graphite/query.go +++ b/pkg/tsdb/graphite/query.go @@ -8,7 +8,6 @@ import ( "io" "net/http" "net/url" - "path" "regexp" "strconv" "strings" @@ -173,7 +172,12 @@ func (s *Service) createGraphiteRequest(ctx context.Context, query backend.DataQ s.logger.Debug("Graphite request", "params", formData) - graphiteReq, err := s.createRequest(ctx, dsInfo, formData) + graphiteReq, err := s.createRequest(ctx, dsInfo, URLParams{ + SubPath: "render", + Method: http.MethodPost, + Body: strings.NewReader(formData.Encode()), + Headers: map[string]string{"Content-Type": "application/x-www-form-urlencoded"}, + }) if err != nil { return nil, formData, nil, err } @@ -181,23 +185,6 @@ func (s *Service) createGraphiteRequest(ctx context.Context, query backend.DataQ return graphiteReq, formData, emptyQuery, nil } -func (s *Service) createRequest(ctx context.Context, dsInfo *datasourceInfo, data url.Values) (*http.Request, error) { - u, err := url.Parse(dsInfo.URL) - if err != nil { - return nil, err - } - u.Path = path.Join(u.Path, "render") - - req, err := http.NewRequestWithContext(ctx, http.MethodPost, u.String(), strings.NewReader(data.Encode())) - if err != nil { - s.logger.Info("Failed to create request", "error", err) - return nil, fmt.Errorf("failed to create request: %w", err) - } - - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - return req, err -} - func (s *Service) toDataFrames(response *http.Response, refId string) (frames data.Frames, error error) { responseData, err := s.parseResponse(response) if err != nil { diff --git a/pkg/tsdb/graphite/resource_handler.go b/pkg/tsdb/graphite/resource_handler.go index 53130cbee6b..6988aafaad3 100644 --- a/pkg/tsdb/graphite/resource_handler.go +++ b/pkg/tsdb/graphite/resource_handler.go @@ -10,21 +10,23 @@ import ( "strings" "github.com/grafana/grafana-plugin-sdk-go/backend" + "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana-plugin-sdk-go/backend/tracing" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" ) -type resourceHandler func(context.Context, *datasourceInfo, []byte) ([]byte, int, error) +type resourceHandler[T any] func(context.Context, *datasourceInfo, T) ([]byte, int, error) func (s *Service) newResourceMux() *http.ServeMux { mux := http.NewServeMux() - mux.HandleFunc("/events", s.handleResourceReq(s.handleEvents)) - mux.HandleFunc("/metrics/find", s.handleResourceReq(s.handleMetricsFind)) + mux.HandleFunc("/events", handleResourceReq[GraphiteEventsRequest](s.handleEvents, s)) + mux.HandleFunc("/metrics/find", handleResourceReq[GraphiteMetricsFindRequest](s.handleMetricsFind, s)) + mux.HandleFunc("/metrics/expand", handleResourceReq[GraphiteMetricsFindRequest](s.handleMetricsExpand, s)) return mux } -func (s *Service) handleResourceReq(handlerFn resourceHandler) func(rw http.ResponseWriter, req *http.Request) { +func handleResourceReq[T any](handlerFn resourceHandler[T], s *Service) func(rw http.ResponseWriter, req *http.Request) { return func(rw http.ResponseWriter, req *http.Request) { s.logger.Debug("Received resource call", "url", req.URL.String(), "method", req.Method) @@ -55,7 +57,13 @@ func (s *Service) handleResourceReq(handlerFn resourceHandler) func(rw http.Resp return } - response, statusCode, err := handlerFn(ctx, dsInfo, requestBody) + parsedBody, err := parseRequestBody[T](requestBody, s.logger) + if err != nil { + writeErrorResponse(rw, http.StatusBadRequest, fmt.Sprintf("failed to parse request body: %v", err)) + return + } + + response, statusCode, err := handlerFn(ctx, dsInfo, *parsedBody) if err != nil { writeErrorResponse(rw, statusCode, fmt.Sprintf("failed to handle resource request: %v", err)) return @@ -70,59 +78,27 @@ func (s *Service) handleResourceReq(handlerFn resourceHandler) func(rw http.Resp } } -func (s *Service) handleEvents(ctx context.Context, dsInfo *datasourceInfo, requestBody []byte) ([]byte, int, error) { - eventsRequestJson := GraphiteEventsRequest{} - err := json.Unmarshal(requestBody, &eventsRequestJson) - if err != nil { - s.logger.Error("Failed to unmarshal events request body to JSON", "error", err) - return nil, http.StatusInternalServerError, fmt.Errorf("unexpected error %v", err) +func (s *Service) handleEvents(ctx context.Context, dsInfo *datasourceInfo, eventsRequestJson GraphiteEventsRequest) ([]byte, int, error) { + queryParams := map[string]string{ + "from": eventsRequestJson.From, + "until": eventsRequestJson.Until, } - - eventsUrl, err := url.Parse(fmt.Sprintf("%s/events/get_data", dsInfo.URL)) - if err != nil { - return nil, http.StatusInternalServerError, fmt.Errorf("unexpected error %v", err) - } - - queryValues := eventsUrl.Query() - queryValues.Set("from", eventsRequestJson.From) - queryValues.Set("until", eventsRequestJson.Until) if eventsRequestJson.Tags != "" { - queryValues.Set("tags", eventsRequestJson.Tags) + queryParams["tags"] = eventsRequestJson.Tags } - eventsUrl.RawQuery = queryValues.Encode() - - graphiteReq, err := http.NewRequestWithContext(ctx, http.MethodGet, eventsUrl.String(), nil) + req, err := s.createRequest(ctx, dsInfo, URLParams{ + SubPath: "events/get_data", + Method: http.MethodGet, + QueryParams: queryParams, + }) if err != nil { - s.logger.Info("Failed to create events request", "error", err) - return nil, http.StatusInternalServerError, fmt.Errorf("failed to create events request: %v", err) + return nil, http.StatusInternalServerError, fmt.Errorf("failed to create events request %v", err) } - _, span := tracing.DefaultTracer().Start(ctx, "graphite events") - defer span.End() - span.SetAttributes( - attribute.Int64("datasource_id", dsInfo.Id), - ) - res, err := dsInfo.HTTPClient.Do(graphiteReq) - if res != nil { - span.SetAttributes(attribute.Int("graphite.response.code", res.StatusCode)) - } + events, statusCode, err := doGraphiteRequest[[]GraphiteEventsResponse](ctx, dsInfo, s.logger, req) if err != nil { - span.RecordError(err) - span.SetStatus(codes.Error, err.Error()) - return nil, http.StatusInternalServerError, fmt.Errorf("failed to complete events request: %v", err) - } - - defer func() { - err := res.Body.Close() - if err != nil { - s.logger.Warn("Failed to close response body", "error", err) - } - }() - - events, err := parseResponse[[]GraphiteEventsResponse](res) - if err != nil { - return nil, http.StatusInternalServerError, fmt.Errorf("failed to parse events response: %v", err) + return nil, statusCode, fmt.Errorf("events request failed: %v", err) } // We construct this struct to avoid frontend changes. @@ -133,68 +109,39 @@ func (s *Service) handleEvents(ctx context.Context, dsInfo *datasourceInfo, requ return nil, http.StatusInternalServerError, fmt.Errorf("failed to marshal events response: %s", err) } - return graphiteEventsResponse, res.StatusCode, nil + return graphiteEventsResponse, statusCode, nil } -func (s *Service) handleMetricsFind(ctx context.Context, dsInfo *datasourceInfo, requestBody []byte) ([]byte, int, error) { - metricsFindRequestJson := GraphiteMetricsFindRequest{} - err := json.Unmarshal(requestBody, &metricsFindRequestJson) - if err != nil { - s.logger.Error("Failed to unmarshal metrics find request body to JSON", "error", err) - return nil, http.StatusInternalServerError, fmt.Errorf("unexpected error %v", err) - } - +func (s *Service) handleMetricsFind(ctx context.Context, dsInfo *datasourceInfo, metricsFindRequestJson GraphiteMetricsFindRequest) ([]byte, int, error) { if metricsFindRequestJson.Query == "" { return nil, http.StatusBadRequest, fmt.Errorf("query is required") } - metricsFindUrl, err := url.Parse(fmt.Sprintf("%s/metrics/find", dsInfo.URL)) - if err != nil { - return nil, http.StatusInternalServerError, fmt.Errorf("unexpected error %v", err) - } - - queryValues := metricsFindUrl.Query() - if metricsFindRequestJson.From != "" { - queryValues.Set("from", metricsFindRequestJson.From) - } - if metricsFindRequestJson.Until != "" { - queryValues.Set("until", metricsFindRequestJson.Until) - } - data := url.Values{} data.Set("query", metricsFindRequestJson.Query) - graphiteReq, err := http.NewRequestWithContext(ctx, http.MethodPost, metricsFindUrl.String(), strings.NewReader(data.Encode())) - if err != nil { - s.logger.Info("Failed to create metrics find request", "error", err) - return nil, http.StatusInternalServerError, fmt.Errorf("failed to create metrics find request: %v", err) + queryParams := map[string]string{} + if metricsFindRequestJson.From != "" { + queryParams["from"] = metricsFindRequestJson.From + } + if metricsFindRequestJson.Until != "" { + queryParams["until"] = metricsFindRequestJson.Until } - graphiteReq.Header.Add("Content-Type", "application/x-www-form-urlencoded") - _, span := tracing.DefaultTracer().Start(ctx, "graphite metrics find") - defer span.End() - span.SetAttributes( - attribute.Int64("datasource_id", dsInfo.Id), - ) - res, err := dsInfo.HTTPClient.Do(graphiteReq) - if res != nil { - span.SetAttributes(attribute.Int("graphite.response.code", res.StatusCode)) - } + req, err := s.createRequest(ctx, dsInfo, URLParams{ + SubPath: "metrics/find", + Method: http.MethodPost, + QueryParams: queryParams, + Body: strings.NewReader(data.Encode()), + Headers: map[string]string{"Content-Type": "application/x-www-form-urlencoded"}, + }) if err != nil { - span.RecordError(err) - span.SetStatus(codes.Error, err.Error()) - return nil, http.StatusInternalServerError, fmt.Errorf("failed to complete metrics find request: %v", err) + return nil, http.StatusInternalServerError, fmt.Errorf("failed to create metrics find request %v", err) } - defer func() { - err := res.Body.Close() - if err != nil { - s.logger.Warn("Failed to close response body", "error", err) - } - }() - metrics, err := parseResponse[[]GraphiteMetricsFindResponse](res) + metrics, statusCode, err := doGraphiteRequest[[]GraphiteMetricsFindResponse](ctx, dsInfo, s.logger, req) if err != nil { - return nil, http.StatusInternalServerError, fmt.Errorf("failed to parse metrics find response: %v", err) + return nil, statusCode, fmt.Errorf("metrics find request failed: %v", err) } metricsFindResponse, err := json.Marshal(*metrics) @@ -202,7 +149,91 @@ func (s *Service) handleMetricsFind(ctx context.Context, dsInfo *datasourceInfo, return nil, http.StatusInternalServerError, fmt.Errorf("failed to marshal metrics find response: %s", err) } - return metricsFindResponse, res.StatusCode, nil + return metricsFindResponse, statusCode, nil +} + +func (s *Service) handleMetricsExpand(ctx context.Context, dsInfo *datasourceInfo, metricsExpandRequestJson GraphiteMetricsFindRequest) ([]byte, int, error) { + if metricsExpandRequestJson.Query == "" { + return nil, http.StatusBadRequest, fmt.Errorf("query is required") + } + + queryParams := map[string]string{ + "query": metricsExpandRequestJson.Query, + } + if metricsExpandRequestJson.From != "" { + queryParams["from"] = metricsExpandRequestJson.From + } + if metricsExpandRequestJson.Until != "" { + queryParams["until"] = metricsExpandRequestJson.Until + } + + req, err := s.createRequest(ctx, dsInfo, URLParams{ + SubPath: "metrics/expand", + Method: http.MethodGet, + QueryParams: queryParams, + }) + if err != nil { + return nil, http.StatusInternalServerError, fmt.Errorf("failed to create metrics expand request %v", err) + } + + metrics, statusCode, err := doGraphiteRequest[GraphiteMetricsExpandResponse](ctx, dsInfo, s.logger, req) + if err != nil { + return nil, statusCode, fmt.Errorf("metrics expand request failed: %v", err) + } + + metricsResponse := make([]GraphiteMetricsFindResponse, 0, len(metrics.Results)) + for _, metric := range metrics.Results { + metricsResponse = append(metricsResponse, GraphiteMetricsFindResponse{ + Text: metric, + }) + } + + metricsExpandResponse, err := json.Marshal(metricsResponse) + if err != nil { + return nil, http.StatusInternalServerError, fmt.Errorf("failed to marshal metrics expand response: %s", err) + } + + return metricsExpandResponse, statusCode, nil +} + +func doGraphiteRequest[T any](ctx context.Context, dsInfo *datasourceInfo, logger log.Logger, req *http.Request) (*T, int, error) { + _, span := tracing.DefaultTracer().Start(ctx, "graphite request") + defer span.End() + span.SetAttributes( + attribute.Int64("datasource_id", dsInfo.Id), + ) + res, err := dsInfo.HTTPClient.Do(req) + if res != nil { + span.SetAttributes(attribute.Int("graphite.response.code", res.StatusCode)) + } + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return nil, http.StatusInternalServerError, fmt.Errorf("failed to complete request: %v", err) + } + + defer func() { + if err := res.Body.Close(); err != nil { + logger.Warn("Failed to close response body", "err", err) + } + }() + + parsedResponse, err := parseResponse[T](res) + if err != nil { + return nil, http.StatusInternalServerError, fmt.Errorf("failed to parse response: %v", err) + } + + return parsedResponse, res.StatusCode, nil +} + +func parseRequestBody[V any](requestBody []byte, logger log.Logger) (*V, error) { + requestJson := new(V) + err := json.Unmarshal(requestBody, &requestJson) + if err != nil { + logger.Error("Failed to unmarshal request body to JSON", "error", err) + return nil, fmt.Errorf("unexpected error %v", err) + } + return requestJson, nil } func parseResponse[V any](res *http.Response) (*V, error) { diff --git a/pkg/tsdb/graphite/resource_handler_test.go b/pkg/tsdb/graphite/resource_handler_test.go index 5857af1e146..51d5a0fb290 100644 --- a/pkg/tsdb/graphite/resource_handler_test.go +++ b/pkg/tsdb/graphite/resource_handler_test.go @@ -58,7 +58,7 @@ func TestHandleEvents(t *testing.T) { tests := []struct { name string dsInfo *datasourceInfo - requestBody []byte + request GraphiteEventsRequest expectedStatus int expectError bool errorContains string @@ -71,11 +71,7 @@ func TestHandleEvents(t *testing.T) { URL: "http://graphite.grafana", HTTPClient: &http.Client{Transport: &mockRoundTripper{respBody: mockResp, status: 200}}, }, - requestBody: func() []byte { - request := GraphiteEventsRequest{From: "now-1h", Until: "now", Tags: "foo"} - body, _ := json.Marshal(request) - return body - }(), + request: GraphiteEventsRequest{From: "now-1h", Until: "now", Tags: "foo"}, expectedStatus: 200, expectError: false, expectedEvents: mockEvents, @@ -87,37 +83,21 @@ func TestHandleEvents(t *testing.T) { URL: "http://graphite.grafana", HTTPClient: &http.Client{Transport: &mockRoundTripper{respBody: mockResp, status: 200}}, }, - requestBody: func() []byte { - request := GraphiteEventsRequest{From: "now-1h", Until: "now"} - body, _ := json.Marshal(request) - return body - }(), + request: GraphiteEventsRequest{From: "now-1h", Until: "now"}, expectedStatus: 200, expectError: false, expectedEvents: mockEvents, }, - { - name: "Invalid request body", - dsInfo: &datasourceInfo{Id: 1, URL: "http://graphite.grafana"}, - requestBody: []byte(`{"invalid": json}`), - expectedStatus: http.StatusInternalServerError, - expectError: true, - errorContains: "unexpected error", - }, { name: "Invalid URL", dsInfo: &datasourceInfo{ Id: 1, URL: "ht tp://invalid url", // Invalid URL }, - requestBody: func() []byte { - request := GraphiteEventsRequest{From: "now-1h", Until: "now"} - body, _ := json.Marshal(request) - return body - }(), + request: GraphiteEventsRequest{From: "now-1h", Until: "now"}, expectedStatus: http.StatusInternalServerError, expectError: true, - errorContains: "unexpected error", + errorContains: "failed to create events request", }, { name: "HTTP client error", @@ -126,14 +106,10 @@ func TestHandleEvents(t *testing.T) { URL: "http://graphite.grafana", HTTPClient: &http.Client{Transport: &mockRoundTripper{err: errors.New("network error")}}, }, - requestBody: func() []byte { - request := GraphiteEventsRequest{From: "now-1h", Until: "now"} - body, _ := json.Marshal(request) - return body - }(), + request: GraphiteEventsRequest{From: "now-1h", Until: "now"}, expectedStatus: http.StatusInternalServerError, expectError: true, - errorContains: "failed to complete events request", + errorContains: "events request failed", }, { name: "Invalid response JSON", @@ -142,14 +118,10 @@ func TestHandleEvents(t *testing.T) { URL: "http://graphite.grafana", HTTPClient: &http.Client{Transport: &mockRoundTripper{respBody: []byte("invalid json"), status: 200}}, }, - requestBody: func() []byte { - request := GraphiteEventsRequest{From: "now-1h", Until: "now"} - body, _ := json.Marshal(request) - return body - }(), + request: GraphiteEventsRequest{From: "now-1h", Until: "now"}, expectedStatus: http.StatusInternalServerError, expectError: true, - errorContains: "failed to parse events response", + errorContains: "events request failed", }, } @@ -157,7 +129,7 @@ func TestHandleEvents(t *testing.T) { t.Run(tt.name, func(t *testing.T) { svc := &Service{logger: log.NewNullLogger()} - respBody, status, err := svc.handleEvents(context.Background(), tt.dsInfo, tt.requestBody) + respBody, status, err := svc.handleEvents(context.Background(), tt.dsInfo, tt.request) assert.Equal(t, tt.expectedStatus, status) @@ -191,7 +163,7 @@ func TestHandleMetricsFind(t *testing.T) { tests := []struct { name string dsInfo *datasourceInfo - requestBody []byte + request GraphiteMetricsFindRequest expectedStatus int expectError bool errorContains string @@ -204,11 +176,7 @@ func TestHandleMetricsFind(t *testing.T) { URL: "http://graphite.grafana", HTTPClient: &http.Client{Transport: &mockRoundTripper{respBody: mockResp, status: 200}}, }, - requestBody: func() []byte { - request := GraphiteMetricsFindRequest{Query: "app.grafana.*"} - body, _ := json.Marshal(request) - return body - }(), + request: GraphiteMetricsFindRequest{Query: "app.grafana.*"}, expectedStatus: 200, expectError: false, expectedMetrics: mockMetrics, @@ -220,35 +188,19 @@ func TestHandleMetricsFind(t *testing.T) { URL: "http://graphite.grafana", HTTPClient: &http.Client{Transport: &mockRoundTripper{respBody: mockResp, status: 200}}, }, - requestBody: func() []byte { - request := GraphiteMetricsFindRequest{ - Query: "app.grafana.*", - From: "now-1h", - Until: "now", - } - body, _ := json.Marshal(request) - return body - }(), + request: GraphiteMetricsFindRequest{ + Query: "app.grafana.*", + From: "now-1h", + Until: "now", + }, expectedStatus: 200, expectError: false, expectedMetrics: mockMetrics, }, { - name: "Invalid request body", + name: "Empty query", dsInfo: &datasourceInfo{Id: 1, URL: "http://graphite.grafana"}, - requestBody: []byte(`{"invalid": json}`), - expectedStatus: http.StatusInternalServerError, - expectError: true, - errorContains: "unexpected error", - }, - { - name: "Empty query", - dsInfo: &datasourceInfo{Id: 1, URL: "http://graphite.grafana"}, - requestBody: func() []byte { - request := GraphiteMetricsFindRequest{Query: ""} - body, _ := json.Marshal(request) - return body - }(), + request: GraphiteMetricsFindRequest{Query: ""}, expectedStatus: http.StatusBadRequest, expectError: true, errorContains: "query is required", @@ -259,14 +211,10 @@ func TestHandleMetricsFind(t *testing.T) { Id: 1, URL: "ht tp://invalid url", // Invalid URL }, - requestBody: func() []byte { - request := GraphiteMetricsFindRequest{Query: "app.grafana.*"} - body, _ := json.Marshal(request) - return body - }(), + request: GraphiteMetricsFindRequest{Query: "app.grafana.*"}, expectedStatus: http.StatusInternalServerError, expectError: true, - errorContains: "unexpected error", + errorContains: "failed to create metrics find request", }, { name: "HTTP client error", @@ -275,14 +223,120 @@ func TestHandleMetricsFind(t *testing.T) { URL: "http://graphite.grafana", HTTPClient: &http.Client{Transport: &mockRoundTripper{err: errors.New("network error")}}, }, - requestBody: func() []byte { - request := GraphiteMetricsFindRequest{Query: "app.grafana.*"} - body, _ := json.Marshal(request) - return body - }(), + request: GraphiteMetricsFindRequest{Query: "app.grafana.*"}, expectedStatus: http.StatusInternalServerError, expectError: true, - errorContains: "failed to complete metrics find request", + errorContains: "metrics find request failed", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + svc := &Service{logger: log.NewNullLogger()} + + respBody, status, err := svc.handleMetricsFind(context.Background(), tt.dsInfo, tt.request) + + assert.Equal(t, tt.expectedStatus, status) + + if tt.expectError { + assert.Error(t, err) + assert.Nil(t, respBody) + if tt.errorContains != "" { + assert.Contains(t, err.Error(), tt.errorContains) + } + } else { + require.NoError(t, err) + assert.NotNil(t, respBody) + + if tt.expectedMetrics != nil { + var result []GraphiteMetricsFindResponse + require.NoError(t, json.Unmarshal(respBody, &result)) + assert.Equal(t, tt.expectedMetrics, result) + } + } + }) + } +} + +func TestHandleMetricsExpand(t *testing.T) { + mockExpandResponse := GraphiteMetricsExpandResponse{ + Results: []string{"app.grafana.metric1", "app.grafana.metric2", "app.grafana.metric3"}, + } + mockResp, _ := json.Marshal(mockExpandResponse) + + expectedMetrics := []GraphiteMetricsFindResponse{ + {Text: "app.grafana.metric1"}, + {Text: "app.grafana.metric2"}, + {Text: "app.grafana.metric3"}, + } + + tests := []struct { + name string + dsInfo *datasourceInfo + request GraphiteMetricsFindRequest + expectedStatus int + expectError bool + errorContains string + expectedMetrics []GraphiteMetricsFindResponse + }{ + { + name: "Success with query", + dsInfo: &datasourceInfo{ + Id: 1, + URL: "http://graphite.grafana", + HTTPClient: &http.Client{Transport: &mockRoundTripper{respBody: mockResp, status: 200}}, + }, + request: GraphiteMetricsFindRequest{Query: "app.grafana.*"}, + expectedStatus: 200, + expectError: false, + expectedMetrics: expectedMetrics, + }, + { + name: "Success with query and time range", + dsInfo: &datasourceInfo{ + Id: 1, + URL: "http://graphite.grafana", + HTTPClient: &http.Client{Transport: &mockRoundTripper{respBody: mockResp, status: 200}}, + }, + request: GraphiteMetricsFindRequest{ + Query: "app.grafana.*", + From: "now-1h", + Until: "now", + }, + expectedStatus: 200, + expectError: false, + expectedMetrics: expectedMetrics, + }, + { + name: "Empty query", + dsInfo: &datasourceInfo{Id: 1, URL: "http://graphite.grafana"}, + request: GraphiteMetricsFindRequest{Query: ""}, + expectedStatus: http.StatusBadRequest, + expectError: true, + errorContains: "query is required", + }, + { + name: "Invalid URL", + dsInfo: &datasourceInfo{ + Id: 1, + URL: "ht tp://invalid url", // Invalid URL + }, + request: GraphiteMetricsFindRequest{Query: "app.grafana.*"}, + expectedStatus: http.StatusInternalServerError, + expectError: true, + errorContains: "failed to create metrics expand request", + }, + { + name: "HTTP client error", + dsInfo: &datasourceInfo{ + Id: 1, + URL: "http://graphite.grafana", + HTTPClient: &http.Client{Transport: &mockRoundTripper{err: errors.New("network error")}}, + }, + request: GraphiteMetricsFindRequest{Query: "app.grafana.*"}, + expectedStatus: http.StatusInternalServerError, + expectError: true, + errorContains: "metrics expand request failed", }, { name: "Invalid response JSON", @@ -291,14 +345,22 @@ func TestHandleMetricsFind(t *testing.T) { URL: "http://graphite.grafana", HTTPClient: &http.Client{Transport: &mockRoundTripper{respBody: []byte("invalid json"), status: 200}}, }, - requestBody: func() []byte { - request := GraphiteMetricsFindRequest{Query: "app.grafana.*"} - body, _ := json.Marshal(request) - return body - }(), + request: GraphiteMetricsFindRequest{Query: "app.grafana.*"}, expectedStatus: http.StatusInternalServerError, expectError: true, - errorContains: "failed to parse metrics find response", + errorContains: "metrics expand request failed", + }, + { + name: "Empty results", + dsInfo: &datasourceInfo{ + Id: 1, + URL: "http://graphite.grafana", + HTTPClient: &http.Client{Transport: &mockRoundTripper{respBody: []byte(`{"results":[]}`), status: 200}}, + }, + request: GraphiteMetricsFindRequest{Query: "nonexistent.*"}, + expectedStatus: 200, + expectError: false, + expectedMetrics: []GraphiteMetricsFindResponse{}, }, } @@ -306,7 +368,7 @@ func TestHandleMetricsFind(t *testing.T) { t.Run(tt.name, func(t *testing.T) { svc := &Service{logger: log.NewNullLogger()} - respBody, status, err := svc.handleMetricsFind(context.Background(), tt.dsInfo, tt.requestBody) + respBody, status, err := svc.handleMetricsExpand(context.Background(), tt.dsInfo, tt.request) assert.Equal(t, tt.expectedStatus, status) @@ -352,7 +414,7 @@ func TestHandleResourceReq_Success(t *testing.T) { req = req.WithContext(backend.WithPluginContext(context.Background(), backend.PluginContext{})) rr := httptest.NewRecorder() - handler := svc.handleResourceReq(svc.handleEvents) + handler := handleResourceReq(svc.handleEvents, svc) handler(rr, req) assert.Equal(t, http.StatusOK, rr.Code) @@ -372,7 +434,7 @@ func TestHandleResourceReq_GetDSInfoError(t *testing.T) { req = req.WithContext(backend.WithPluginContext(context.Background(), backend.PluginContext{})) rr := httptest.NewRecorder() - handler := svc.handleResourceReq(svc.handleEvents) + handler := handleResourceReq(svc.handleEvents, svc) handler(rr, req) assert.Equal(t, http.StatusInternalServerError, rr.Code) @@ -394,7 +456,7 @@ func TestHandleResourceReq_NilHandler(t *testing.T) { req = req.WithContext(backend.WithPluginContext(context.Background(), backend.PluginContext{})) rr := httptest.NewRecorder() - handler := svc.handleResourceReq(nil) + handler := handleResourceReq[any](nil, svc) handler(rr, req) assert.Equal(t, http.StatusInternalServerError, rr.Code) @@ -414,3 +476,368 @@ func TestWriteErrorResponse(t *testing.T) { require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &errorResp)) assert.Equal(t, "test error message", errorResp["error"]) } + +func TestDoGraphiteRequest(t *testing.T) { + mockResponse := []GraphiteEventsResponse{ + {When: 1234567890, What: "event1", Tags: []string{"tag1"}, Data: "data1"}, + } + mockResp, _ := json.Marshal(mockResponse) + + tests := []struct { + name string + endpoint string + dsInfo *datasourceInfo + method string + body io.Reader + headers map[string]string + expectedStatus int + expectError bool + errorContains string + expectedData []GraphiteEventsResponse + }{ + { + name: "Success GET request", + endpoint: "events", + dsInfo: &datasourceInfo{ + Id: 1, + URL: "http://graphite.grafana", + HTTPClient: &http.Client{Transport: &mockRoundTripper{respBody: mockResp, status: 200}}, + }, + method: "GET", + headers: map[string]string{"Content-Type": "application/json"}, + expectedStatus: 200, + expectError: false, + expectedData: mockResponse, + }, + { + name: "Success POST request with body", + endpoint: "events", + dsInfo: &datasourceInfo{ + Id: 1, + URL: "http://graphite.grafana", + HTTPClient: &http.Client{Transport: &mockRoundTripper{respBody: mockResp, status: 200}}, + }, + method: "POST", + body: bytes.NewReader([]byte("query=test")), + headers: map[string]string{"Content-Type": "application/x-www-form-urlencoded"}, + expectedStatus: 200, + expectError: false, + expectedData: mockResponse, + }, + { + name: "HTTP client error", + endpoint: "events", + dsInfo: &datasourceInfo{ + Id: 1, + URL: "http://graphite.grafana", + HTTPClient: &http.Client{Transport: &mockRoundTripper{err: errors.New("network error")}}, + }, + method: "GET", + headers: map[string]string{}, + expectError: true, + errorContains: "failed to complete request", + }, + { + name: "Invalid response JSON", + endpoint: "events", + dsInfo: &datasourceInfo{ + Id: 1, + URL: "http://graphite.grafana", + HTTPClient: &http.Client{Transport: &mockRoundTripper{respBody: []byte("invalid json"), status: 200}}, + }, + method: "GET", + headers: map[string]string{}, + expectError: true, + errorContains: "failed to parse response", + }, + { + name: "Non-200 status code with valid JSON", + endpoint: "events", + dsInfo: &datasourceInfo{ + Id: 1, + URL: "http://graphite.grafana", + HTTPClient: &http.Client{Transport: &mockRoundTripper{respBody: []byte("[]"), status: 500}}, + }, + method: "GET", + headers: map[string]string{}, + expectedStatus: 500, + expectError: false, + expectedData: []GraphiteEventsResponse{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() + + // Create a service instance for the test + svc := &Service{logger: log.NewNullLogger()} + + // Create the HTTP request using the createRequest method + req, err := svc.createRequest(ctx, tt.dsInfo, URLParams{ + SubPath: tt.endpoint, + Method: tt.method, + Body: tt.body, + Headers: tt.headers, + }) + + if tt.expectError { + // For cases where we expect errors in request creation + if err != nil { + assert.Error(t, err) + if tt.errorContains != "" { + assert.Contains(t, err.Error(), tt.errorContains) + } + return + } + } else { + assert.NoError(t, err) + } + + result, status, err := doGraphiteRequest[[]GraphiteEventsResponse](ctx, tt.dsInfo, svc.logger, req) + + if tt.expectError { + assert.Error(t, err) + assert.Nil(t, result) + if tt.errorContains != "" { + assert.Contains(t, err.Error(), tt.errorContains) + } + } else { + assert.NoError(t, err) + assert.NotNil(t, result) + if tt.expectedStatus != 0 { + assert.Equal(t, tt.expectedStatus, status) + } + if tt.expectedData != nil { + assert.Equal(t, tt.expectedData, *result) + } + } + }) + } +} + +func TestDoGraphiteRequestGenericTypes(t *testing.T) { + // Test with GraphiteMetricsFindResponse + mockMetrics := []GraphiteMetricsFindResponse{ + {Text: "metric1", Id: "metric1.id", AllowChildren: 1, Expandable: 1, Leaf: 0}, + } + mockMetricsResp, _ := json.Marshal(mockMetrics) + + // Test with GraphiteMetricsExpandResponse + mockExpand := GraphiteMetricsExpandResponse{ + Results: []string{"app.grafana.metric1", "app.grafana.metric2"}, + } + mockExpandResp, _ := json.Marshal(mockExpand) + + tests := []struct { + name string + testFunc func(t *testing.T) + }{ + { + name: "Success with GraphiteMetricsFindResponse type", + testFunc: func(t *testing.T) { + dsInfo := &datasourceInfo{ + Id: 1, + URL: "http://graphite.grafana", + HTTPClient: &http.Client{Transport: &mockRoundTripper{respBody: mockMetricsResp, status: 200}}, + } + ctx := context.Background() + + // Create a service instance for the test + svc := &Service{logger: log.NewNullLogger()} + + // Create the HTTP request using the createRequest method + req, err := svc.createRequest(ctx, dsInfo, URLParams{ + SubPath: "test", + Method: "GET", + }) + assert.NoError(t, err) + + result, status, err := doGraphiteRequest[[]GraphiteMetricsFindResponse](ctx, dsInfo, svc.logger, req) + + assert.NoError(t, err) + assert.NotNil(t, result) + assert.Equal(t, 200, status) + assert.Equal(t, mockMetrics, *result) + }, + }, + { + name: "Success with GraphiteMetricsExpandResponse type", + testFunc: func(t *testing.T) { + dsInfo := &datasourceInfo{ + Id: 1, + URL: "http://graphite.grafana", + HTTPClient: &http.Client{Transport: &mockRoundTripper{respBody: mockExpandResp, status: 200}}, + } + ctx := context.Background() + + // Create a service instance for the test + svc := &Service{logger: log.NewNullLogger()} + + // Create the HTTP request using the createRequest method + req, err := svc.createRequest(ctx, dsInfo, URLParams{ + SubPath: "test", + Method: "GET", + }) + assert.NoError(t, err) + + result, status, err := doGraphiteRequest[GraphiteMetricsExpandResponse](ctx, dsInfo, svc.logger, req) + + assert.NoError(t, err) + assert.NotNil(t, result) + assert.Equal(t, 200, status) + assert.Equal(t, mockExpand, *result) + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, tt.testFunc) + } +} + +func TestParseRequestBody(t *testing.T) { + tests := []struct { + name string + requestBody []byte + expectError bool + errorContains string + expectedData GraphiteEventsRequest + }{ + { + name: "Valid JSON request", + requestBody: []byte(`{"from": "now-1h", "until": "now", "tags": "app.grafana"}`), + expectError: false, + expectedData: GraphiteEventsRequest{From: "now-1h", Until: "now", Tags: "app.grafana"}, + }, + { + name: "Empty JSON object", + requestBody: []byte(`{}`), + expectError: false, + expectedData: GraphiteEventsRequest{}, + }, + { + name: "Invalid JSON", + requestBody: []byte(`{"invalid": json}`), + expectError: true, + errorContains: "unexpected error", + }, + { + name: "Empty request body", + requestBody: []byte(``), + expectError: true, + errorContains: "unexpected error", + }, + { + name: "Malformed JSON", + requestBody: []byte(`{"from": "now-1h", "until": }`), + expectError: true, + errorContains: "unexpected error", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + logger := log.NewNullLogger() + + result, err := parseRequestBody[GraphiteEventsRequest](tt.requestBody, logger) + + if tt.expectError { + assert.Error(t, err) + assert.Nil(t, result) + if tt.errorContains != "" { + assert.Contains(t, err.Error(), tt.errorContains) + } + } else { + assert.NoError(t, err) + assert.NotNil(t, result) + assert.Equal(t, tt.expectedData, *result) + } + }) + } +} + +func TestParseResponse(t *testing.T) { + mockEvents := []GraphiteEventsResponse{ + {When: 1234567890, What: "event1", Tags: []string{"tag1"}, Data: "data1"}, + {When: 1234567891, What: "event2", Tags: []string{"tag2"}, Data: "data2"}, + } + mockResp, _ := json.Marshal(mockEvents) + + tests := []struct { + name string + response *http.Response + expectError bool + errorContains string + expectedData []GraphiteEventsResponse + }{ + { + name: "Valid JSON response", + response: &http.Response{ + StatusCode: 200, + Body: io.NopCloser(bytes.NewBuffer(mockResp)), + Header: make(http.Header), + }, + expectError: false, + expectedData: mockEvents, + }, + { + name: "Empty JSON array", + response: &http.Response{ + StatusCode: 200, + Body: io.NopCloser(bytes.NewBuffer([]byte("[]"))), + Header: make(http.Header), + }, + expectError: false, + expectedData: []GraphiteEventsResponse{}, + }, + { + name: "Invalid JSON response", + response: &http.Response{ + StatusCode: 200, + Body: io.NopCloser(bytes.NewBuffer([]byte("invalid json"))), + Header: make(http.Header), + }, + expectError: true, + errorContains: "failed to unmarshal response", + }, + { + name: "Empty response body", + response: &http.Response{ + StatusCode: 200, + Body: io.NopCloser(bytes.NewBuffer([]byte(""))), + Header: make(http.Header), + }, + expectError: true, + errorContains: "failed to unmarshal response", + }, + { + name: "Malformed JSON response", + response: &http.Response{ + StatusCode: 200, + Body: io.NopCloser(bytes.NewBuffer([]byte(`[{"when": 123, "what": }]`))), + Header: make(http.Header), + }, + expectError: true, + errorContains: "failed to unmarshal response", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := parseResponse[[]GraphiteEventsResponse](tt.response) + + if tt.expectError { + assert.Error(t, err) + assert.Nil(t, result) + if tt.errorContains != "" { + assert.Contains(t, err.Error(), tt.errorContains) + } + } else { + assert.NoError(t, err) + assert.NotNil(t, result) + assert.Equal(t, tt.expectedData, *result) + } + }) + } +} diff --git a/pkg/tsdb/graphite/types.go b/pkg/tsdb/graphite/types.go index 8bc46177cf0..30d872f375a 100644 --- a/pkg/tsdb/graphite/types.go +++ b/pkg/tsdb/graphite/types.go @@ -1,5 +1,7 @@ package graphite +import "io" + type TargetResponseDTO struct { Target string `json:"target"` DataPoints DataTimeSeriesPoints `json:"datapoints"` @@ -10,6 +12,14 @@ type TargetResponseDTO struct { type DataTimePoint [2]Float type DataTimeSeriesPoints []DataTimePoint +type URLParams struct { + SubPath string + Method string + Body io.Reader + QueryParams map[string]string + Headers map[string]string +} + type GraphiteQuery struct { QueryType string `json:"queryType"` TextEditor *bool `json:"textEditor,omitempty"` @@ -45,3 +55,7 @@ type GraphiteMetricsFindResponse struct { Expandable int `json:"expandable"` Leaf int `json:"leaf"` } + +type GraphiteMetricsExpandResponse struct { + Results []string `json:"results"` +} diff --git a/public/app/plugins/datasource/graphite/datasource.ts b/public/app/plugins/datasource/graphite/datasource.ts index 2a51d0107d6..7f04fa713af 100644 --- a/public/app/plugins/datasource/graphite/datasource.ts +++ b/public/app/plugins/datasource/graphite/datasource.ts @@ -704,8 +704,8 @@ export class GraphiteDatasource if (config.featureToggles.graphiteBackendMode) { return await this.postResource('metrics/find', { - from: typeof params.from === 'string' ? params.from : `${params.from}`, - until: typeof params.until === 'string' ? params.until : `${params.until}`, + from: params.from ? (typeof params.from === 'string' ? params.from : `${params.from}`) : undefined, + until: params.until ? (typeof params.until === 'string' ? params.until : `${params.until}`) : undefined, query, }); } @@ -741,7 +741,7 @@ export class GraphiteDatasource * The result will contain all metrics (with full name) matching provided query. * It's a more flexible version of /metrics/find endpoint (@see requestMetricFind) */ - private requestMetricExpand( + private async requestMetricExpand( query: string, requestId: string, range?: { from: string | number; until: string | number } @@ -752,6 +752,18 @@ export class GraphiteDatasource params.until = range.until; } + if (config.featureToggles.graphiteBackendMode) { + const metrics = await this.postResource('metrics/expand', { + from: params.from ? (typeof params.from === 'string' ? params.from : `${params.from}`) : undefined, + until: params.until ? (typeof params.until === 'string' ? params.until : `${params.until}`) : undefined, + query, + }); + return metrics.map((metric) => ({ + text: metric.text, + expandable: false, + })); + } + const httpOptions: BackendSrvRequest = { method: 'GET', url: '/metrics/expand', From cb7abbaa0f70888bc9c68f642e4cc78a91b90900 Mon Sep 17 00:00:00 2001 From: Moustafa Baiou Date: Fri, 12 Sep 2025 18:15:55 -0400 Subject: [PATCH 12/33] Alerting: Rename expression elements of Rules APIs (#110914) This renames `data` to `expressions` for clarity in the rules apis. Also makes certain fields that are redundant optional in the case of pure expressions, so that users don't have to specify them when they are not needed (e.g. not datasource queries). --- .../rules/definitions/alerting-manifest.yaml | 42 ++- .../alertrule.rules.alerting.grafana.app.yaml | 29 +- ...ordingrule.rules.alerting.grafana.app.yaml | 13 +- .../rules/kinds/v0alpha1/alertRule_spec.cue | 14 +- .../rules/kinds/v0alpha1/rule_spec.cue | 23 +- .../alerting/v0alpha1/alertrule_spec_gen.go | 76 +++-- .../v0alpha1/recordingrule_spec_gen.go | 62 ++-- .../apis/alerting/v0alpha1/zz_openapi_gen.go | 322 +++++++++--------- .../rules/pkg/apis/alerting_manifest.go | 4 +- .../alertrule/v0alpha1/types.spec.gen.ts | 76 +++-- .../recordingrule/v0alpha1/types.spec.gen.ts | 60 ++-- .../apps/alerting/rules/alertrule/compat.go | 74 ++-- .../alerting/rules/recordingrule/compat.go | 110 +++--- .../rules/alertrule/alertrule_test.go | 52 +-- .../alerting/rules/compat/alertrule_test.go | 30 +- .../rules/compat/recordingrule_test.go | 22 +- .../rules/recordingrule/recordingrule_test.go | 52 +-- .../rules.alerting.grafana.app-v0alpha1.json | 40 +-- .../clients/rules/v0alpha1/endpoints.gen.ts | 26 +- 19 files changed, 610 insertions(+), 517 deletions(-) diff --git a/apps/alerting/rules/definitions/alerting-manifest.yaml b/apps/alerting/rules/definitions/alerting-manifest.yaml index f842608a627..2c7b9374a66 100644 --- a/apps/alerting/rules/definitions/alerting-manifest.yaml +++ b/apps/alerting/rules/definitions/alerting-manifest.yaml @@ -19,14 +19,24 @@ spec: additionalProperties: type: string type: object - data: + execErrState: + default: Error + enum: + - Error + - Ok + - Alerting + - KeepLast + type: string + expressions: additionalProperties: properties: datasourceUID: + description: The UID of the datasource to run this expression against. If omitted, the expression will be run against the `__expr__` datasource pattern: ^[a-zA-Z0-9_-]+$ type: string model: {} queryType: + description: The type of query if this is a query expression type: string relativeTimeRange: properties: @@ -41,21 +51,16 @@ spec: - to type: object source: + description: |- + Used to mark the expression to be used as the final source for the rule evaluation + Only one expression in a rule can be marked as the source + For AlertRules, this is the expression that will be evaluated against the alerting condition + For RecordingRules, this is the expression that will be recorded type: boolean required: - - queryType - - datasourceUID - model type: object type: object - execErrState: - default: Error - enum: - - Error - - Ok - - Alerting - - KeepLast - type: string for: allOf: - pattern: ^((([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?|0)$ @@ -151,10 +156,10 @@ spec: type: object required: - title - - data - trigger - noDataState - execErrState + - expressions type: object x-kubernetes-preserve-unknown-fields: true status: @@ -207,14 +212,16 @@ spec: schema: spec: properties: - data: + expressions: additionalProperties: properties: datasourceUID: + description: The UID of the datasource to run this expression against. If omitted, the expression will be run against the `__expr__` datasource pattern: ^[a-zA-Z0-9_-]+$ type: string model: {} queryType: + description: The type of query if this is a query expression type: string relativeTimeRange: properties: @@ -229,10 +236,13 @@ spec: - to type: object source: + description: |- + Used to mark the expression to be used as the final source for the rule evaluation + Only one expression in a rule can be marked as the source + For AlertRules, this is the expression that will be evaluated against the alerting condition + For RecordingRules, this is the expression that will be recorded type: boolean required: - - queryType - - datasourceUID - model type: object type: object @@ -263,9 +273,9 @@ spec: type: object required: - title - - data - trigger - metric + - expressions - targetDatasourceUID type: object x-kubernetes-preserve-unknown-fields: true diff --git a/apps/alerting/rules/definitions/alertrule.rules.alerting.grafana.app.yaml b/apps/alerting/rules/definitions/alertrule.rules.alerting.grafana.app.yaml index e7d9ab25aaa..e5fdfc57e5e 100644 --- a/apps/alerting/rules/definitions/alertrule.rules.alerting.grafana.app.yaml +++ b/apps/alerting/rules/definitions/alertrule.rules.alerting.grafana.app.yaml @@ -17,14 +17,24 @@ spec: additionalProperties: type: string type: object - data: + execErrState: + default: Error + enum: + - Error + - Ok + - Alerting + - KeepLast + type: string + expressions: additionalProperties: properties: datasourceUID: + description: The UID of the datasource to run this expression against. If omitted, the expression will be run against the `__expr__` datasource pattern: ^[a-zA-Z0-9_-]+$ type: string model: {} queryType: + description: The type of query if this is a query expression type: string relativeTimeRange: properties: @@ -39,21 +49,16 @@ spec: - to type: object source: + description: |- + Used to mark the expression to be used as the final source for the rule evaluation + Only one expression in a rule can be marked as the source + For AlertRules, this is the expression that will be evaluated against the alerting condition + For RecordingRules, this is the expression that will be recorded type: boolean required: - - queryType - - datasourceUID - model type: object type: object - execErrState: - default: Error - enum: - - Error - - Ok - - Alerting - - KeepLast - type: string for: allOf: - pattern: ^((([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?|0)$ @@ -149,10 +154,10 @@ spec: type: object required: - title - - data - trigger - noDataState - execErrState + - expressions type: object x-kubernetes-preserve-unknown-fields: true status: diff --git a/apps/alerting/rules/definitions/recordingrule.rules.alerting.grafana.app.yaml b/apps/alerting/rules/definitions/recordingrule.rules.alerting.grafana.app.yaml index b12bdf781f9..368bc5893c0 100644 --- a/apps/alerting/rules/definitions/recordingrule.rules.alerting.grafana.app.yaml +++ b/apps/alerting/rules/definitions/recordingrule.rules.alerting.grafana.app.yaml @@ -13,14 +13,16 @@ spec: properties: spec: properties: - data: + expressions: additionalProperties: properties: datasourceUID: + description: The UID of the datasource to run this expression against. If omitted, the expression will be run against the `__expr__` datasource pattern: ^[a-zA-Z0-9_-]+$ type: string model: {} queryType: + description: The type of query if this is a query expression type: string relativeTimeRange: properties: @@ -35,10 +37,13 @@ spec: - to type: object source: + description: |- + Used to mark the expression to be used as the final source for the rule evaluation + Only one expression in a rule can be marked as the source + For AlertRules, this is the expression that will be evaluated against the alerting condition + For RecordingRules, this is the expression that will be recorded type: boolean required: - - queryType - - datasourceUID - model type: object type: object @@ -69,9 +74,9 @@ spec: type: object required: - title - - data - trigger - metric + - expressions - targetDatasourceUID type: object x-kubernetes-preserve-unknown-fields: true diff --git a/apps/alerting/rules/kinds/v0alpha1/alertRule_spec.cue b/apps/alerting/rules/kinds/v0alpha1/alertRule_spec.cue index 0d074d28e6f..140005fe1d6 100644 --- a/apps/alerting/rules/kinds/v0alpha1/alertRule_spec.cue +++ b/apps/alerting/rules/kinds/v0alpha1/alertRule_spec.cue @@ -10,16 +10,16 @@ ExecErrState: *"Error" | "Ok" | "Alerting" | "KeepLast" // FIXME: the For and KeepFiringFor types should be using the AlertRulePromDuration type, but there seems to be an issue with the generator AlertRuleSpec: #RuleSpec & { - noDataState: NoDataState - execErrState: ExecErrState - "for"?: string & #PromDuration - keepFiringFor?: string & #PromDuration - missingSeriesEvalsToResolve?: int & >=0 - notificationSettings?: #NotificationSettings annotations?: { [string]: TemplateString } - panelRef?: #PanelRef + "for"?: string & #PromDuration + keepFiringFor?: string & #PromDuration + missingSeriesEvalsToResolve?: int & >=0 + noDataState: NoDataState + execErrState: ExecErrState + notificationSettings?: #NotificationSettings + panelRef?: #PanelRef } #PanelRef: { diff --git a/apps/alerting/rules/kinds/v0alpha1/rule_spec.cue b/apps/alerting/rules/kinds/v0alpha1/rule_spec.cue index 11c45d3b8fb..7b8cf55e734 100644 --- a/apps/alerting/rules/kinds/v0alpha1/rule_spec.cue +++ b/apps/alerting/rules/kinds/v0alpha1/rule_spec.cue @@ -12,12 +12,12 @@ TemplateString: string #RuleSpec: { title: string - data: #QueryMap paused?: bool trigger: #IntervalTrigger labels?: { [string]: TemplateString } + expressions: #ExpressionMap ... } @@ -34,15 +34,20 @@ TemplateString: string } // TODO: validate that only one can specify source=true -#QueryMap: { - [string]: #Query +#ExpressionMap: { + [string]: #Expression } // & struct.MinFields(1) This doesn't work in Cue ({ + interval: defaultPromDuration(), +}); + +export type PromDuration = string; + +export const defaultPromDuration = (): PromDuration => (""); + +export type TemplateString = string; + +export const defaultTemplateString = (): TemplateString => (""); + +// TODO(@moustafab): validate regex for time interval ref +export type TimeIntervalRef = string; + +export const defaultTimeIntervalRef = (): TimeIntervalRef => (""); + // TODO: validate that only one can specify source=true // & struct.MinFields(1) This doesn't work in Cue ; +export type ExpressionMap = Record; -export const defaultQueryMap = (): QueryMap => ({}); +export const defaultExpressionMap = (): ExpressionMap => ({}); -// TODO: come up with a better name for this. We have expression type things and data source queries -export interface Query { - // TODO: consider making this optional, with the nil value meaning "__expr__" (i.e. expression query) - queryType: string; +export interface Expression { + // The type of query if this is a query expression + queryType?: string; relativeTimeRange?: RelativeTimeRange; - datasourceUID: DatasourceUID; + // The UID of the datasource to run this expression against. If omitted, the expression will be run against the `__expr__` datasource + datasourceUID?: DatasourceUID; model: any; + // Used to mark the expression to be used as the final source for the rule evaluation + // Only one expression in a rule can be marked as the source + // For AlertRules, this is the expression that will be evaluated against the alerting condition + // For RecordingRules, this is the expression that will be recorded source?: boolean; } -export const defaultQuery = (): Query => ({ - queryType: "", - datasourceUID: defaultDatasourceUID(), +export const defaultExpression = (): Expression => ({ model: {}, }); @@ -40,37 +63,17 @@ export type DatasourceUID = string; export const defaultDatasourceUID = (): DatasourceUID => (""); -export interface IntervalTrigger { - interval: PromDuration; -} - -export const defaultIntervalTrigger = (): IntervalTrigger => ({ - interval: defaultPromDuration(), -}); - -export type PromDuration = string; - -export const defaultPromDuration = (): PromDuration => (""); - -// TODO(@moustafab): validate regex for time interval ref -export type TimeIntervalRef = string; - -export const defaultTimeIntervalRef = (): TimeIntervalRef => (""); - -export type TemplateString = string; - -export const defaultTemplateString = (): TemplateString => (""); - export interface Spec { title: string; - data: QueryMap; paused?: boolean; trigger: IntervalTrigger; - noDataState: string; - execErrState: string; + labels?: Record; + annotations?: Record; for?: string; keepFiringFor?: string; missingSeriesEvalsToResolve?: number; + noDataState: string; + execErrState: string; notificationSettings?: { receiver: string; groupBy?: string[]; @@ -80,8 +83,7 @@ export interface Spec { muteTimeIntervals?: TimeIntervalRef[]; activeTimeIntervals?: TimeIntervalRef[]; }; - annotations?: Record; - labels?: Record; + expressions: ExpressionMap; panelRef?: { dashboardUID: string; panelID: number; @@ -90,9 +92,9 @@ export interface Spec { export const defaultSpec = (): Spec => ({ title: "", - data: defaultQueryMap(), trigger: defaultIntervalTrigger(), noDataState: "NoData", execErrState: "Error", + expressions: defaultExpressionMap(), }); diff --git a/apps/alerting/rules/plugin/src/generated/recordingrule/v0alpha1/types.spec.gen.ts b/apps/alerting/rules/plugin/src/generated/recordingrule/v0alpha1/types.spec.gen.ts index f9944c8b4d7..fc8923debe6 100644 --- a/apps/alerting/rules/plugin/src/generated/recordingrule/v0alpha1/types.spec.gen.ts +++ b/apps/alerting/rules/plugin/src/generated/recordingrule/v0alpha1/types.spec.gen.ts @@ -1,24 +1,42 @@ // Code generated - EDITING IS FUTILE. DO NOT EDIT. +export interface IntervalTrigger { + interval: PromDuration; +} + +export const defaultIntervalTrigger = (): IntervalTrigger => ({ + interval: defaultPromDuration(), +}); + +export type PromDuration = string; + +export const defaultPromDuration = (): PromDuration => (""); + +export type TemplateString = string; + +export const defaultTemplateString = (): TemplateString => (""); + // TODO: validate that only one can specify source=true // & struct.MinFields(1) This doesn't work in Cue ; +export type ExpressionMap = Record; -export const defaultQueryMap = (): QueryMap => ({}); +export const defaultExpressionMap = (): ExpressionMap => ({}); -// TODO: come up with a better name for this. We have expression type things and data source queries -export interface Query { - // TODO: consider making this optional, with the nil value meaning "__expr__" (i.e. expression query) - queryType: string; +export interface Expression { + // The type of query if this is a query expression + queryType?: string; relativeTimeRange?: RelativeTimeRange; - datasourceUID: DatasourceUID; + // The UID of the datasource to run this expression against. If omitted, the expression will be run against the `__expr__` datasource + datasourceUID?: DatasourceUID; model: any; + // Used to mark the expression to be used as the final source for the rule evaluation + // Only one expression in a rule can be marked as the source + // For AlertRules, this is the expression that will be evaluated against the alerting condition + // For RecordingRules, this is the expression that will be recorded source?: boolean; } -export const defaultQuery = (): Query => ({ - queryType: "", - datasourceUID: defaultDatasourceUID(), +export const defaultExpression = (): Expression => ({ model: {}, }); @@ -40,37 +58,21 @@ export type DatasourceUID = string; export const defaultDatasourceUID = (): DatasourceUID => (""); -export interface IntervalTrigger { - interval: PromDuration; -} - -export const defaultIntervalTrigger = (): IntervalTrigger => ({ - interval: defaultPromDuration(), -}); - -export type PromDuration = string; - -export const defaultPromDuration = (): PromDuration => (""); - -export type TemplateString = string; - -export const defaultTemplateString = (): TemplateString => (""); - export interface Spec { title: string; - data: QueryMap; paused?: boolean; trigger: IntervalTrigger; - metric: string; labels?: Record; + metric: string; + expressions: ExpressionMap; targetDatasourceUID: string; } export const defaultSpec = (): Spec => ({ title: "", - data: defaultQueryMap(), trigger: defaultIntervalTrigger(), metric: "", + expressions: defaultExpressionMap(), targetDatasourceUID: "", }); diff --git a/pkg/registry/apps/alerting/rules/alertrule/compat.go b/pkg/registry/apps/alerting/rules/alertrule/compat.go index fbbb2be73d7..0ea9507c6e0 100644 --- a/pkg/registry/apps/alerting/rules/alertrule/compat.go +++ b/pkg/registry/apps/alerting/rules/alertrule/compat.go @@ -8,6 +8,7 @@ import ( "time" "github.com/grafana/grafana/pkg/apimachinery/utils" + "github.com/grafana/grafana/pkg/expr" "github.com/grafana/grafana/pkg/util" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -43,8 +44,8 @@ func convertToK8sResource( Labels: make(map[string]string), }, Spec: model.AlertRuleSpec{ - Title: rule.Title, - Data: make(map[string]model.AlertRuleQuery), + Title: rule.Title, + Expressions: make(model.AlertRuleExpressionMap), Trigger: model.AlertRuleIntervalTrigger{ Interval: model.AlertRulePromDuration(interval.String()), }, @@ -90,19 +91,7 @@ func convertToK8sResource( } for _, query := range rule.Data { - k8sQuery := model.AlertRuleQuery{ - QueryType: query.QueryType, - Model: query.Model, - DatasourceUID: model.AlertRuleDatasourceUID(query.DatasourceUID), - Source: util.Pointer(rule.Condition == query.RefID), - } - if time.Duration(query.RelativeTimeRange.From) > 0 || time.Duration(query.RelativeTimeRange.To) > 0 { - k8sQuery.RelativeTimeRange = &model.AlertRuleRelativeTimeRange{ - From: model.AlertRulePromDurationWMillis(query.RelativeTimeRange.From.String()), - To: model.AlertRulePromDurationWMillis(query.RelativeTimeRange.To.String()), - } - } - k8sRule.Spec.Data[query.RefID] = k8sQuery + k8sRule.Spec.Expressions[query.RefID] = convertToK8sExpression(query, rule) } for _, setting := range rule.NotificationSettings { @@ -158,6 +147,29 @@ func convertToK8sResource( return k8sRule, nil } +func convertToK8sExpression(query ngmodels.AlertQuery, rule *ngmodels.AlertRule) model.AlertRuleExpression { + expression := model.AlertRuleExpression{ + Model: query.Model, + } + if query.QueryType != "" { + expression.QueryType = util.Pointer(query.QueryType) + } + // DatasourceUID is optional and defaults to expr datasource + if !expr.IsDataSource(query.DatasourceUID) { + expression.DatasourceUID = util.Pointer(model.AlertRuleDatasourceUID(query.DatasourceUID)) + } + if time.Duration(query.RelativeTimeRange.From) > 0 || time.Duration(query.RelativeTimeRange.To) > 0 { + expression.RelativeTimeRange = &model.AlertRuleRelativeTimeRange{ + From: model.AlertRulePromDurationWMillis(query.RelativeTimeRange.From.String()), + To: model.AlertRulePromDurationWMillis(query.RelativeTimeRange.To.String()), + } + } + if rule.Condition == query.RefID { + expression.Source = util.Pointer(true) + } + return expression +} + func convertToK8sResources( orgID int64, rules []*ngmodels.AlertRule, @@ -201,7 +213,7 @@ func convertToBaseDomainModel(orgID int64, k8sRule *model.AlertRule) (*ngmodels. UID: k8sRule.Name, Title: k8sRule.Spec.Title, NamespaceUID: k8sRule.Namespace, - Data: make([]ngmodels.AlertQuery, 0, len(k8sRule.Spec.Data)), + Data: make([]ngmodels.AlertQuery, 0, len(k8sRule.Spec.Expressions)), IsPaused: k8sRule.Spec.Paused != nil && *k8sRule.Spec.Paused, Labels: make(map[string]string), Annotations: make(map[string]string), @@ -267,13 +279,13 @@ func convertToBaseDomainModel(orgID int64, k8sRule *model.AlertRule) (*ngmodels. } domainRule.IntervalSeconds = int64(time.Duration(interval).Seconds()) - for refID, query := range k8sRule.Spec.Data { - domainQuery, err := convertToDomainQuery(query, refID) + for refID, expression := range k8sRule.Spec.Expressions { + domainQuery, err := convertToDomainQuery(expression, refID) if err != nil { return nil, err } domainRule.Data = append(domainRule.Data, domainQuery) - if query.Source != nil && *query.Source { + if expression.Source != nil && *expression.Source { if domainRule.Condition != "" { return nil, fmt.Errorf("multiple queries marked as source: %s and %s", domainRule.Condition, refID) } @@ -339,23 +351,29 @@ func convertNotificationSettings(sourceSettings *model.AlertRuleV0alpha1SpecNoti return settings, nil } -func convertToDomainQuery(query model.AlertRuleQuery, refID string) (ngmodels.AlertQuery, error) { - modelJson, err := json.Marshal(query.Model) +func convertToDomainQuery(expression model.AlertRuleExpression, refID string) (ngmodels.AlertQuery, error) { + modelJson, err := json.Marshal(expression.Model) if err != nil { return ngmodels.AlertQuery{}, fmt.Errorf("failed to marshal model: %w", err) } domainQuery := ngmodels.AlertQuery{ - RefID: refID, - QueryType: query.QueryType, - DatasourceUID: string(query.DatasourceUID), - Model: modelJson, + RefID: refID, + Model: modelJson, } - if query.RelativeTimeRange != nil { - from, err := prom_model.ParseDuration(string(query.RelativeTimeRange.From)) + if expression.QueryType != nil { + domainQuery.QueryType = *expression.QueryType + } + if expression.DatasourceUID != nil { + domainQuery.DatasourceUID = string(*expression.DatasourceUID) + } else { + domainQuery.DatasourceUID = expr.DatasourceUID + } + if expression.RelativeTimeRange != nil { + from, err := prom_model.ParseDuration(string(expression.RelativeTimeRange.From)) if err != nil { return ngmodels.AlertQuery{}, fmt.Errorf("failed to parse duration: %w", err) } - to, err := prom_model.ParseDuration(string(query.RelativeTimeRange.To)) + to, err := prom_model.ParseDuration(string(expression.RelativeTimeRange.To)) if err != nil { return ngmodels.AlertQuery{}, fmt.Errorf("failed to parse duration: %w", err) } diff --git a/pkg/registry/apps/alerting/rules/recordingrule/compat.go b/pkg/registry/apps/alerting/rules/recordingrule/compat.go index f4479a41d48..e9ef0c4e295 100644 --- a/pkg/registry/apps/alerting/rules/recordingrule/compat.go +++ b/pkg/registry/apps/alerting/rules/recordingrule/compat.go @@ -9,6 +9,7 @@ import ( model "github.com/grafana/grafana/apps/alerting/rules/pkg/apis/alerting/v0alpha1" "github.com/grafana/grafana/pkg/apimachinery/utils" + "github.com/grafana/grafana/pkg/expr" "github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" gapiutil "github.com/grafana/grafana/pkg/services/apiserver/utils" ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models" @@ -42,8 +43,8 @@ func convertToK8sResource( Labels: make(map[string]string), }, Spec: model.RecordingRuleSpec{ - Title: rule.Title, - Data: make(map[string]model.RecordingRuleQuery), + Title: rule.Title, + Expressions: make(model.RecordingRuleExpressionMap), Trigger: model.RecordingRuleIntervalTrigger{ Interval: model.RecordingRulePromDuration(interval.String()), }, @@ -67,21 +68,7 @@ func convertToK8sResource( } for _, query := range rule.Data { - k8sQuery := model.RecordingRuleQuery{ - QueryType: query.QueryType, - Model: query.Model, - DatasourceUID: model.RecordingRuleDatasourceUID(query.DatasourceUID), - } - if time.Duration(query.RelativeTimeRange.From) > 0 || time.Duration(query.RelativeTimeRange.To) > 0 { - k8sQuery.RelativeTimeRange = &model.RecordingRuleRelativeTimeRange{ - From: model.RecordingRulePromDurationWMillis(query.RelativeTimeRange.From.String()), - To: model.RecordingRulePromDurationWMillis(query.RelativeTimeRange.To.String()), - } - } - if rule.Record != nil && rule.Record.From == query.RefID { - k8sQuery.Source = util.Pointer(true) - } - k8sRule.Spec.Data[query.RefID] = k8sQuery + k8sRule.Spec.Expressions[query.RefID] = convertToK8sExpression(query, rule) } meta, err := utils.MetaAccessor(k8sRule) @@ -108,6 +95,29 @@ func convertToK8sResource( return k8sRule, nil } +func convertToK8sExpression(query ngmodels.AlertQuery, rule *ngmodels.AlertRule) model.RecordingRuleExpression { + expression := model.RecordingRuleExpression{ + Model: query.Model, + } + if query.QueryType != "" { + expression.QueryType = util.Pointer(query.QueryType) + } + // DatasourceUID is optional and defaults to expr datasource + if !expr.IsDataSource(query.DatasourceUID) { + expression.DatasourceUID = util.Pointer(model.RecordingRuleDatasourceUID(query.DatasourceUID)) + } + if time.Duration(query.RelativeTimeRange.From) > 0 || time.Duration(query.RelativeTimeRange.To) > 0 { + expression.RelativeTimeRange = &model.RecordingRuleRelativeTimeRange{ + From: model.RecordingRulePromDurationWMillis(query.RelativeTimeRange.From.String()), + To: model.RecordingRulePromDurationWMillis(query.RelativeTimeRange.To.String()), + } + } + if rule.Record != nil && rule.Record.From == query.RefID { + expression.Source = util.Pointer(true) + } + return expression +} + func convertToK8sResources( orgID int64, rules []*ngmodels.AlertRule, @@ -150,7 +160,7 @@ func convertToBaseDomainModel(orgID int64, k8sRule *model.RecordingRule) (*ngmod OrgID: orgID, UID: k8sRule.Name, Title: k8sRule.Spec.Title, - Data: make([]ngmodels.AlertQuery, 0, len(k8sRule.Spec.Data)), + Data: make([]ngmodels.AlertQuery, 0, len(k8sRule.Spec.Expressions)), IsPaused: k8sRule.Spec.Paused != nil && *k8sRule.Spec.Paused, Labels: make(map[string]string), @@ -187,35 +197,13 @@ func convertToBaseDomainModel(orgID int64, k8sRule *model.RecordingRule) (*ngmod for k, v := range k8sRule.Spec.Labels { domainRule.Labels[k] = string(v) } - for refID, query := range k8sRule.Spec.Data { - modelJson, err := json.Marshal(query.Model) + for refID, expression := range k8sRule.Spec.Expressions { + domainQuery, err := convertToDomainQuery(expression, refID) if err != nil { - return nil, fmt.Errorf("failed to marshal model: %w", err) + return nil, err } - domainQuery := ngmodels.AlertQuery{ - RefID: refID, - QueryType: query.QueryType, - DatasourceUID: string(query.DatasourceUID), - Model: modelJson, - } - if query.RelativeTimeRange != nil { - from, err := prom_model.ParseDuration(string(query.RelativeTimeRange.From)) - if err != nil { - return nil, fmt.Errorf("failed to parse duration: %w", err) - } - to, err := prom_model.ParseDuration(string(query.RelativeTimeRange.To)) - if err != nil { - return nil, fmt.Errorf("failed to parse duration: %w", err) - } - domainQuery.RelativeTimeRange = ngmodels.RelativeTimeRange{ - From: ngmodels.Duration(from), - To: ngmodels.Duration(to), - } - } - domainRule.Data = append(domainRule.Data, domainQuery) - - if query.Source != nil && *query.Source { + if expression.Source != nil && *expression.Source { if domainRule.Record.From != "" { return nil, fmt.Errorf("multiple queries marked as source: %s and %s", domainRule.Record.From, refID) } @@ -227,3 +215,37 @@ func convertToBaseDomainModel(orgID int64, k8sRule *model.RecordingRule) (*ngmod } return domainRule, nil } + +func convertToDomainQuery(expression model.RecordingRuleExpression, refID string) (ngmodels.AlertQuery, error) { + modelJson, err := json.Marshal(expression.Model) + if err != nil { + return ngmodels.AlertQuery{}, fmt.Errorf("failed to marshal model: %w", err) + } + domainQuery := ngmodels.AlertQuery{ + RefID: refID, + Model: modelJson, + } + if expression.QueryType != nil { + domainQuery.QueryType = *expression.QueryType + } + if expression.DatasourceUID != nil { + domainQuery.DatasourceUID = string(*expression.DatasourceUID) + } else { + domainQuery.DatasourceUID = expr.DatasourceUID + } + if expression.RelativeTimeRange != nil { + from, err := prom_model.ParseDuration(string(expression.RelativeTimeRange.From)) + if err != nil { + return ngmodels.AlertQuery{}, fmt.Errorf("failed to parse duration: %w", err) + } + to, err := prom_model.ParseDuration(string(expression.RelativeTimeRange.To)) + if err != nil { + return ngmodels.AlertQuery{}, fmt.Errorf("failed to parse duration: %w", err) + } + domainQuery.RelativeTimeRange = ngmodels.RelativeTimeRange{ + From: ngmodels.Duration(from), + To: ngmodels.Duration(to), + } + } + return domainQuery, nil +} diff --git a/pkg/tests/apis/alerting/rules/alertrule/alertrule_test.go b/pkg/tests/apis/alerting/rules/alertrule/alertrule_test.go index 9daedc259ae..5d4a8b67497 100644 --- a/pkg/tests/apis/alerting/rules/alertrule/alertrule_test.go +++ b/pkg/tests/apis/alerting/rules/alertrule/alertrule_test.go @@ -54,10 +54,10 @@ func TestIntegrationResourceIdentifier(t *testing.T) { }, Spec: v0alpha1.AlertRuleSpec{ Title: rule.Title, - Data: map[string]v0alpha1.AlertRuleQuery{ + Expressions: v0alpha1.AlertRuleExpressionMap{ "A": { - QueryType: "query", - DatasourceUID: v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID), + QueryType: util.Pointer("query"), + DatasourceUID: util.Pointer(v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID)), Model: rule.Data[0].Model, Source: util.Pointer(true), RelativeTimeRange: &v0alpha1.AlertRuleRelativeTimeRange{ @@ -154,10 +154,10 @@ func TestIntegrationAccessControl(t *testing.T) { }, Spec: v0alpha1.AlertRuleSpec{ Title: rule.Title, - Data: map[string]v0alpha1.AlertRuleQuery{ + Expressions: v0alpha1.AlertRuleExpressionMap{ "A": { - QueryType: "query", - DatasourceUID: v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID), + QueryType: util.Pointer(rule.Data[0].QueryType), + DatasourceUID: util.Pointer(v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID)), Model: rule.Data[0].Model, Source: util.Pointer(true), RelativeTimeRange: &v0alpha1.AlertRuleRelativeTimeRange{ @@ -241,10 +241,10 @@ func TestIntegrationCRUD(t *testing.T) { }, Spec: v0alpha1.AlertRuleSpec{ Title: rule.Title, - Data: map[string]v0alpha1.AlertRuleQuery{ + Expressions: v0alpha1.AlertRuleExpressionMap{ "A": { - QueryType: "query", - DatasourceUID: v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID), + QueryType: util.Pointer(rule.Data[0].QueryType), + DatasourceUID: util.Pointer(v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID)), Model: rule.Data[0].Model, Source: util.Pointer(true), RelativeTimeRange: &v0alpha1.AlertRuleRelativeTimeRange{ @@ -295,10 +295,10 @@ func TestIntegrationCRUD(t *testing.T) { }, Spec: v0alpha1.AlertRuleSpec{ Title: rule.Title, - Data: map[string]v0alpha1.AlertRuleQuery{ + Expressions: v0alpha1.AlertRuleExpressionMap{ "A": { - QueryType: "query", - DatasourceUID: v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID), + QueryType: util.Pointer(rule.Data[0].QueryType), + DatasourceUID: util.Pointer(v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID)), Model: rule.Data[0].Model, Source: util.Pointer(true), RelativeTimeRange: &v0alpha1.AlertRuleRelativeTimeRange{ @@ -328,8 +328,8 @@ func TestIntegrationCRUD(t *testing.T) { }, }, Spec: v0alpha1.AlertRuleSpec{ - Title: "invalid-rule", - Data: map[string]v0alpha1.AlertRuleQuery{}, // Empty data should fail + Title: "invalid-rule", + Expressions: v0alpha1.AlertRuleExpressionMap{}, // Empty data should fail Trigger: v0alpha1.AlertRuleIntervalTrigger{ Interval: "30", }, @@ -356,10 +356,10 @@ func TestIntegrationCRUD(t *testing.T) { }, Spec: v0alpha1.AlertRuleSpec{ Title: rule.Title, - Data: map[string]v0alpha1.AlertRuleQuery{ + Expressions: v0alpha1.AlertRuleExpressionMap{ "A": { - QueryType: "query", - DatasourceUID: v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID), + QueryType: util.Pointer(rule.Data[0].QueryType), + DatasourceUID: util.Pointer(v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID)), Model: rule.Data[0].Model, Source: util.Pointer(true), RelativeTimeRange: &v0alpha1.AlertRuleRelativeTimeRange{ @@ -408,10 +408,10 @@ func TestIntegrationCRUD(t *testing.T) { }, Spec: v0alpha1.AlertRuleSpec{ Title: rule.Title, - Data: map[string]v0alpha1.AlertRuleQuery{ + Expressions: v0alpha1.AlertRuleExpressionMap{ "A": { - QueryType: "query", - DatasourceUID: v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID), + QueryType: util.Pointer(rule.Data[0].QueryType), + DatasourceUID: util.Pointer(v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID)), Model: rule.Data[0].Model, RelativeTimeRange: &v0alpha1.AlertRuleRelativeTimeRange{ From: v0alpha1.AlertRulePromDurationWMillis("5m"), @@ -445,10 +445,10 @@ func TestIntegrationCRUD(t *testing.T) { }, Spec: v0alpha1.AlertRuleSpec{ Title: rule.Title, - Data: map[string]v0alpha1.AlertRuleQuery{ + Expressions: v0alpha1.AlertRuleExpressionMap{ "A": { - QueryType: "query", - DatasourceUID: v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID), + QueryType: util.Pointer(rule.Data[0].QueryType), + DatasourceUID: util.Pointer(v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID)), Model: rule.Data[0].Model, Source: util.Pointer(true), RelativeTimeRange: &v0alpha1.AlertRuleRelativeTimeRange{ @@ -501,10 +501,10 @@ func TestIntegrationPatch(t *testing.T) { }, Spec: v0alpha1.AlertRuleSpec{ Title: rule.Title, - Data: map[string]v0alpha1.AlertRuleQuery{ + Expressions: v0alpha1.AlertRuleExpressionMap{ "A": { - QueryType: "query", - DatasourceUID: v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID), + QueryType: util.Pointer(rule.Data[0].QueryType), + DatasourceUID: util.Pointer(v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID)), Model: rule.Data[0].Model, Source: util.Pointer(true), RelativeTimeRange: &v0alpha1.AlertRuleRelativeTimeRange{ diff --git a/pkg/tests/apis/alerting/rules/compat/alertrule_test.go b/pkg/tests/apis/alerting/rules/compat/alertrule_test.go index d5339bdf7c7..4eb2cc39b52 100644 --- a/pkg/tests/apis/alerting/rules/compat/alertrule_test.go +++ b/pkg/tests/apis/alerting/rules/compat/alertrule_test.go @@ -57,10 +57,10 @@ func TestIntegrationAlertRuleCompatCreateViaK8s(t *testing.T) { }, Spec: v0alpha1.AlertRuleSpec{ Title: rule.Title, - Data: map[string]v0alpha1.AlertRuleQuery{ + Expressions: v0alpha1.AlertRuleExpressionMap{ "A": { - QueryType: "query", - DatasourceUID: v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID), + QueryType: util.Pointer(rule.Data[0].QueryType), + DatasourceUID: util.Pointer(v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID)), Model: rule.Data[0].Model, Source: util.Pointer(true), RelativeTimeRange: &v0alpha1.AlertRuleRelativeTimeRange{ @@ -91,9 +91,9 @@ func TestIntegrationAlertRuleCompatCreateViaK8s(t *testing.T) { err := json.Unmarshal(retrievedRule.Data[0].Model, &model) require.NoError(t, err) require.NotNil(t, model) - expectedModel, ok := created.Spec.Data["A"].Model.(map[string]interface{}) + expectedModel, ok := created.Spec.Expressions["A"].Model.(map[string]interface{}) if !ok { - t.Fatalf("Expected model to be a map[string]interface{}, got %T", created.Spec.Data["A"].Model) + t.Fatalf("Expected model to be a map[string]interface{}, got %T", created.Spec.Expressions["A"].Model) } for k, v := range expectedModel { require.EqualValues(t, v, model[k], "Model field %s should match", k) @@ -230,13 +230,13 @@ func TestIntegrationAlertRuleCompatCreateViaProvisioning(t *testing.T) { require.NoError(t, err) require.NotNil(t, retrievedRule) require.Equal(t, r.Title, retrievedRule.Spec.Title) - require.NotNil(t, retrievedRule.Spec.Data[r.Data[0].RefID].Source) - require.True(t, *retrievedRule.Spec.Data[r.Data[0].RefID].Source) + require.NotNil(t, retrievedRule.Spec.Expressions[r.Data[0].RefID].Source) + require.True(t, *retrievedRule.Spec.Expressions[r.Data[0].RefID].Source) require.Equal(t, r.FolderUID, retrievedRule.Annotations["grafana.app/folder"]) require.Equal(t, created.Title, retrievedRule.Labels[v0alpha1.GroupLabelKey]) require.Equal(t, fmt.Sprintf("%d", i), retrievedRule.Labels[v0alpha1.GroupIndexLabelKey]) require.Equal(t, ngmodels.ProvenanceAPI, ngmodels.Provenance(retrievedRule.GetProvenanceStatus())) - require.EqualValues(t, r.Data[0].DatasourceUID, retrievedRule.Spec.Data["A"].DatasourceUID) + require.EqualValues(t, r.Data[0].DatasourceUID, *retrievedRule.Spec.Expressions["A"].DatasourceUID) expectedDuration, err := prom_model.ParseDuration(fmt.Sprintf("%ds", created.Interval)) require.NoError(t, err) require.Equal(t, expectedDuration.String(), string(retrievedRule.Spec.Trigger.Interval)) @@ -244,9 +244,9 @@ func TestIntegrationAlertRuleCompatCreateViaProvisioning(t *testing.T) { err = json.Unmarshal(r.Data[0].Model, &expectedModel) require.NoError(t, err) require.NotNil(t, expectedModel) - retrievedModel, ok := retrievedRule.Spec.Data["A"].Model.(map[string]interface{}) + retrievedModel, ok := retrievedRule.Spec.Expressions["A"].Model.(map[string]interface{}) if !ok { - t.Fatalf("Expected model to be a map[string]interface{}, got %T", retrievedRule.Spec.Data["A"].Model) + t.Fatalf("Expected model to be a map[string]interface{}, got %T", retrievedRule.Spec.Expressions["A"].Model) } for k, v := range expectedModel { require.EqualValues(t, v, retrievedModel[k], "Model field %s should match", k) @@ -372,13 +372,13 @@ func TestIntegrationAlertRuleCompatCreateViaProvisioningChangeGroupInK8s(t *test require.NoError(t, err) require.NotNil(t, retrievedRule) require.Equal(t, r.Title, retrievedRule.Spec.Title) - require.NotNil(t, retrievedRule.Spec.Data[r.Data[0].RefID].Source) - require.True(t, *retrievedRule.Spec.Data[r.Data[0].RefID].Source) + require.NotNil(t, retrievedRule.Spec.Expressions[r.Data[0].RefID].Source) + require.True(t, *retrievedRule.Spec.Expressions[r.Data[0].RefID].Source) require.Equal(t, r.FolderUID, retrievedRule.Annotations["grafana.app/folder"]) require.Equal(t, created.Title, retrievedRule.Labels[v0alpha1.GroupLabelKey]) require.Equal(t, fmt.Sprintf("%d", i), retrievedRule.Labels[v0alpha1.GroupIndexLabelKey]) require.Equal(t, ngmodels.ProvenanceAPI, ngmodels.Provenance(retrievedRule.GetProvenanceStatus())) - require.EqualValues(t, r.Data[0].DatasourceUID, retrievedRule.Spec.Data["X"].DatasourceUID) + require.EqualValues(t, r.Data[0].DatasourceUID, *retrievedRule.Spec.Expressions["X"].DatasourceUID) expectedDuration, err := prom_model.ParseDuration(fmt.Sprintf("%ds", created.Interval)) require.NoError(t, err) require.Equal(t, expectedDuration.String(), string(retrievedRule.Spec.Trigger.Interval)) @@ -386,9 +386,9 @@ func TestIntegrationAlertRuleCompatCreateViaProvisioningChangeGroupInK8s(t *test err = json.Unmarshal(r.Data[0].Model, &expectedModel) require.NoError(t, err) require.NotNil(t, expectedModel) - retrievedModel, ok := retrievedRule.Spec.Data["X"].Model.(map[string]interface{}) + retrievedModel, ok := retrievedRule.Spec.Expressions["X"].Model.(map[string]interface{}) if !ok { - t.Fatalf("Expected model to be a map[string]interface{}, got %T", retrievedRule.Spec.Data["X"].Model) + t.Fatalf("Expected model to be a map[string]interface{}, got %T", retrievedRule.Spec.Expressions["X"].Model) } for k, v := range expectedModel { require.EqualValues(t, v, retrievedModel[k], "Model field %s should match", k) diff --git a/pkg/tests/apis/alerting/rules/compat/recordingrule_test.go b/pkg/tests/apis/alerting/rules/compat/recordingrule_test.go index a254901c72f..25f4dbf243b 100644 --- a/pkg/tests/apis/alerting/rules/compat/recordingrule_test.go +++ b/pkg/tests/apis/alerting/rules/compat/recordingrule_test.go @@ -64,10 +64,10 @@ func TestIntegrationRecordingRuleCompatCreateViaK8s(t *testing.T) { Spec: v0alpha1.RecordingRuleSpec{ Title: rule.Title, Metric: rule.Record.Metric, - Data: map[string]v0alpha1.RecordingRuleQuery{ + Expressions: v0alpha1.RecordingRuleExpressionMap{ "A": { - QueryType: "query", - DatasourceUID: v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID), + QueryType: util.Pointer(rule.Data[0].QueryType), + DatasourceUID: util.Pointer(v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID)), Model: rule.Data[0].Model, Source: util.Pointer(true), RelativeTimeRange: &v0alpha1.RecordingRuleRelativeTimeRange{ @@ -98,9 +98,9 @@ func TestIntegrationRecordingRuleCompatCreateViaK8s(t *testing.T) { err := json.Unmarshal(retrievedRule.Data[0].Model, &model) require.NoError(t, err) require.NotNil(t, model) - expectedModel, ok := created.Spec.Data["A"].Model.(map[string]interface{}) + expectedModel, ok := created.Spec.Expressions["A"].Model.(map[string]interface{}) if !ok { - t.Fatalf("Expected model to be a map[string]interface{}, got %T", created.Spec.Data["A"].Model) + t.Fatalf("Expected model to be a map[string]interface{}, got %T", created.Spec.Expressions["A"].Model) } for k, v := range expectedModel { require.EqualValues(t, v, model[k], "Model field %s should match", k) @@ -247,7 +247,7 @@ func TestIntegrationRecordingRuleCompatCreateViaProvisioning(t *testing.T) { require.Equal(t, created.Title, retrievedRule.Labels[v0alpha1.GroupLabelKey]) require.Equal(t, fmt.Sprintf("%d", i), retrievedRule.Labels[v0alpha1.GroupIndexLabelKey]) require.Equal(t, ngmodels.ProvenanceAPI, ngmodels.Provenance(retrievedRule.GetProvenanceStatus())) - require.EqualValues(t, r.Data[0].DatasourceUID, retrievedRule.Spec.Data["A"].DatasourceUID) + require.EqualValues(t, r.Data[0].DatasourceUID, *retrievedRule.Spec.Expressions["A"].DatasourceUID) expectedDuration, err := prom_model.ParseDuration(fmt.Sprintf("%ds", created.Interval)) require.NoError(t, err) require.Equal(t, expectedDuration.String(), string(retrievedRule.Spec.Trigger.Interval)) @@ -255,9 +255,9 @@ func TestIntegrationRecordingRuleCompatCreateViaProvisioning(t *testing.T) { err = json.Unmarshal(r.Data[0].Model, &expectedModel) require.NoError(t, err) require.NotNil(t, expectedModel) - retrievedModel, ok := retrievedRule.Spec.Data["A"].Model.(map[string]interface{}) + retrievedModel, ok := retrievedRule.Spec.Expressions["A"].Model.(map[string]interface{}) if !ok { - t.Fatalf("Expected model to be a map[string]interface{}, got %T", retrievedRule.Spec.Data["A"].Model) + t.Fatalf("Expected model to be a map[string]interface{}, got %T", retrievedRule.Spec.Expressions["A"].Model) } for k, v := range expectedModel { require.EqualValues(t, v, retrievedModel[k], "Model field %s should match", k) @@ -391,7 +391,7 @@ func TestIntegrationRecordingRuleCompatCreateViaProvisioningChangeGroupInK8s(t * require.Equal(t, created.Title, retrievedRule.Labels[v0alpha1.GroupLabelKey]) require.Equal(t, fmt.Sprintf("%d", i), retrievedRule.Labels[v0alpha1.GroupIndexLabelKey]) require.Equal(t, ngmodels.ProvenanceAPI, ngmodels.Provenance(retrievedRule.GetProvenanceStatus())) - require.EqualValues(t, r.Data[0].DatasourceUID, retrievedRule.Spec.Data["X"].DatasourceUID) + require.EqualValues(t, r.Data[0].DatasourceUID, *retrievedRule.Spec.Expressions["X"].DatasourceUID) expectedDuration, err := prom_model.ParseDuration(fmt.Sprintf("%ds", created.Interval)) require.NoError(t, err) require.Equal(t, expectedDuration.String(), string(retrievedRule.Spec.Trigger.Interval)) @@ -399,9 +399,9 @@ func TestIntegrationRecordingRuleCompatCreateViaProvisioningChangeGroupInK8s(t * err = json.Unmarshal(r.Data[0].Model, &expectedModel) require.NoError(t, err) require.NotNil(t, expectedModel) - retrievedModel, ok := retrievedRule.Spec.Data["X"].Model.(map[string]interface{}) + retrievedModel, ok := retrievedRule.Spec.Expressions["X"].Model.(map[string]interface{}) if !ok { - t.Fatalf("Expected model to be a map[string]interface{}, got %T", retrievedRule.Spec.Data["X"].Model) + t.Fatalf("Expected model to be a map[string]interface{}, got %T", retrievedRule.Spec.Expressions["X"].Model) } for k, v := range expectedModel { require.EqualValues(t, v, retrievedModel[k], "Model field %s should match", k) diff --git a/pkg/tests/apis/alerting/rules/recordingrule/recordingrule_test.go b/pkg/tests/apis/alerting/rules/recordingrule/recordingrule_test.go index df706099124..1f024e9fb7f 100644 --- a/pkg/tests/apis/alerting/rules/recordingrule/recordingrule_test.go +++ b/pkg/tests/apis/alerting/rules/recordingrule/recordingrule_test.go @@ -55,10 +55,10 @@ func TestIntegrationResourceIdentifier(t *testing.T) { Spec: v0alpha1.RecordingRuleSpec{ Title: rule.Title, Metric: rule.Record.Metric, - Data: map[string]v0alpha1.RecordingRuleQuery{ + Expressions: v0alpha1.RecordingRuleExpressionMap{ "A": { - QueryType: "query", - DatasourceUID: v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID), + QueryType: util.Pointer(rule.Data[0].QueryType), + DatasourceUID: util.Pointer(v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID)), Model: rule.Data[0].Model, Source: util.Pointer(true), RelativeTimeRange: &v0alpha1.RecordingRuleRelativeTimeRange{ @@ -155,10 +155,10 @@ func TestIntegrationAccessControl(t *testing.T) { Spec: v0alpha1.RecordingRuleSpec{ Title: rule.Title, Metric: rule.Record.Metric, - Data: map[string]v0alpha1.RecordingRuleQuery{ + Expressions: v0alpha1.RecordingRuleExpressionMap{ "A": { - QueryType: "query", - DatasourceUID: v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID), + QueryType: util.Pointer(rule.Data[0].QueryType), + DatasourceUID: util.Pointer(v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID)), Model: rule.Data[0].Model, Source: util.Pointer(true), RelativeTimeRange: &v0alpha1.RecordingRuleRelativeTimeRange{ @@ -242,10 +242,10 @@ func TestIntegrationCRUD(t *testing.T) { Spec: v0alpha1.RecordingRuleSpec{ Title: rule.Title, Metric: rule.Record.Metric, - Data: map[string]v0alpha1.RecordingRuleQuery{ + Expressions: v0alpha1.RecordingRuleExpressionMap{ "A": { - QueryType: "query", - DatasourceUID: v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID), + QueryType: util.Pointer(rule.Data[0].QueryType), + DatasourceUID: util.Pointer(v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID)), Model: rule.Data[0].Model, Source: util.Pointer(true), RelativeTimeRange: &v0alpha1.RecordingRuleRelativeTimeRange{ @@ -294,10 +294,10 @@ func TestIntegrationCRUD(t *testing.T) { Spec: v0alpha1.RecordingRuleSpec{ Title: rule.Title, Metric: rule.Record.Metric, - Data: map[string]v0alpha1.RecordingRuleQuery{ + Expressions: v0alpha1.RecordingRuleExpressionMap{ "A": { - QueryType: "query", - DatasourceUID: v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID), + QueryType: util.Pointer(rule.Data[0].QueryType), + DatasourceUID: util.Pointer(v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID)), Model: rule.Data[0].Model, Source: util.Pointer(true), RelativeTimeRange: &v0alpha1.RecordingRuleRelativeTimeRange{ @@ -325,8 +325,8 @@ func TestIntegrationCRUD(t *testing.T) { }, }, Spec: v0alpha1.RecordingRuleSpec{ - Title: "invalid-recording-rule", - Data: map[string]v0alpha1.RecordingRuleQuery{}, // Empty data should fail + Title: "invalid-recording-rule", + Expressions: v0alpha1.RecordingRuleExpressionMap{}, // Empty data should fail Trigger: v0alpha1.RecordingRuleIntervalTrigger{ Interval: "30s", }, @@ -352,10 +352,10 @@ func TestIntegrationCRUD(t *testing.T) { Spec: v0alpha1.RecordingRuleSpec{ Title: rule.Title, Metric: rule.Record.Metric, - Data: map[string]v0alpha1.RecordingRuleQuery{ + Expressions: v0alpha1.RecordingRuleExpressionMap{ "A": { - QueryType: "query", - DatasourceUID: v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID), + QueryType: util.Pointer(rule.Data[0].QueryType), + DatasourceUID: util.Pointer(v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID)), Model: rule.Data[0].Model, Source: util.Pointer(true), RelativeTimeRange: &v0alpha1.RecordingRuleRelativeTimeRange{ @@ -404,10 +404,10 @@ func TestIntegrationCRUD(t *testing.T) { Spec: v0alpha1.RecordingRuleSpec{ Title: rule.Title, Metric: rule.Record.Metric, - Data: map[string]v0alpha1.RecordingRuleQuery{ + Expressions: v0alpha1.RecordingRuleExpressionMap{ "A": { - QueryType: "query", - DatasourceUID: v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID), + QueryType: util.Pointer(rule.Data[0].QueryType), + DatasourceUID: util.Pointer(v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID)), Model: rule.Data[0].Model, RelativeTimeRange: &v0alpha1.RecordingRuleRelativeTimeRange{ From: v0alpha1.RecordingRulePromDurationWMillis("5m"), @@ -440,10 +440,10 @@ func TestIntegrationCRUD(t *testing.T) { Spec: v0alpha1.RecordingRuleSpec{ Title: rule.Title, Metric: rule.Record.Metric, - Data: map[string]v0alpha1.RecordingRuleQuery{ + Expressions: v0alpha1.RecordingRuleExpressionMap{ "A": { - QueryType: "query", - DatasourceUID: v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID), + QueryType: util.Pointer(rule.Data[0].QueryType), + DatasourceUID: util.Pointer(v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID)), Model: rule.Data[0].Model, Source: util.Pointer(true), RelativeTimeRange: &v0alpha1.RecordingRuleRelativeTimeRange{ @@ -496,10 +496,10 @@ func TestIntegrationPatch(t *testing.T) { Spec: v0alpha1.RecordingRuleSpec{ Title: rule.Title, Metric: rule.Record.Metric, - Data: map[string]v0alpha1.RecordingRuleQuery{ + Expressions: v0alpha1.RecordingRuleExpressionMap{ "A": { - QueryType: "query", - DatasourceUID: v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID), + QueryType: util.Pointer(rule.Data[0].QueryType), + DatasourceUID: util.Pointer(v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID)), Model: rule.Data[0].Model, Source: util.Pointer(true), RelativeTimeRange: &v0alpha1.RecordingRuleRelativeTimeRange{ diff --git a/pkg/tests/apis/openapi_snapshots/rules.alerting.grafana.app-v0alpha1.json b/pkg/tests/apis/openapi_snapshots/rules.alerting.grafana.app-v0alpha1.json index d26d36d1225..afd80701b8c 100644 --- a/pkg/tests/apis/openapi_snapshots/rules.alerting.grafana.app-v0alpha1.json +++ b/pkg/tests/apis/openapi_snapshots/rules.alerting.grafana.app-v0alpha1.json @@ -2369,10 +2369,10 @@ "type": "object", "required": [ "title", - "data", "trigger", "noDataState", - "execErrState" + "execErrState", + "expressions" ], "properties": { "annotations": { @@ -2381,22 +2381,32 @@ "type": "string" } }, - "data": { + "execErrState": { + "type": "string", + "default": "Error", + "enum": [ + "Error", + "Ok", + "Alerting", + "KeepLast" + ] + }, + "expressions": { "type": "object", "additionalProperties": { "type": "object", "required": [ - "queryType", - "datasourceUID", "model" ], "properties": { "datasourceUID": { + "description": "The UID of the datasource to run this expression against. If omitted, the expression will be run against the `__expr__` datasource", "type": "string", "pattern": "^[a-zA-Z0-9_-]+$" }, "model": {}, "queryType": { + "description": "The type of query if this is a query expression", "type": "string" }, "relativeTimeRange": { @@ -2417,21 +2427,12 @@ } }, "source": { + "description": "Used to mark the expression to be used as the final source for the rule evaluation\nOnly one expression in a rule can be marked as the source\nFor AlertRules, this is the expression that will be evaluated against the alerting condition\nFor RecordingRules, this is the expression that will be recorded", "type": "boolean" } } } }, - "execErrState": { - "type": "string", - "default": "Error", - "enum": [ - "Error", - "Ok", - "Alerting", - "KeepLast" - ] - }, "for": { "type": "string", "allOf": [ @@ -2730,28 +2731,28 @@ "type": "object", "required": [ "title", - "data", "trigger", "metric", + "expressions", "targetDatasourceUID" ], "properties": { - "data": { + "expressions": { "type": "object", "additionalProperties": { "type": "object", "required": [ - "queryType", - "datasourceUID", "model" ], "properties": { "datasourceUID": { + "description": "The UID of the datasource to run this expression against. If omitted, the expression will be run against the `__expr__` datasource", "type": "string", "pattern": "^[a-zA-Z0-9_-]+$" }, "model": {}, "queryType": { + "description": "The type of query if this is a query expression", "type": "string" }, "relativeTimeRange": { @@ -2772,6 +2773,7 @@ } }, "source": { + "description": "Used to mark the expression to be used as the final source for the rule evaluation\nOnly one expression in a rule can be marked as the source\nFor AlertRules, this is the expression that will be evaluated against the alerting condition\nFor RecordingRules, this is the expression that will be recorded", "type": "boolean" } } diff --git a/public/app/api/clients/rules/v0alpha1/endpoints.gen.ts b/public/app/api/clients/rules/v0alpha1/endpoints.gen.ts index 01247d97dd1..604940505db 100644 --- a/public/app/api/clients/rules/v0alpha1/endpoints.gen.ts +++ b/public/app/api/clients/rules/v0alpha1/endpoints.gen.ts @@ -846,19 +846,25 @@ export type AlertRuleSpec = { annotations?: { [key: string]: string; }; - data: { + execErrState: 'Error' | 'Ok' | 'Alerting' | 'KeepLast'; + expressions: { [key: string]: { - datasourceUID: string; + /** The UID of the datasource to run this expression against. If omitted, the expression will be run against the `__expr__` datasource */ + datasourceUID?: string; model: any; - queryType: string; + /** The type of query if this is a query expression */ + queryType?: string; relativeTimeRange?: { from: string; to: string; }; + /** Used to mark the expression to be used as the final source for the rule evaluation + Only one expression in a rule can be marked as the source + For AlertRules, this is the expression that will be evaluated against the alerting condition + For RecordingRules, this is the expression that will be recorded */ source?: boolean; }; }; - execErrState: 'Error' | 'Ok' | 'Alerting' | 'KeepLast'; for?: any & any; keepFiringFor?: any & any; labels?: { @@ -982,15 +988,21 @@ export type Status = { }; export type Patch = object; export type RecordingRuleSpec = { - data: { + expressions: { [key: string]: { - datasourceUID: string; + /** The UID of the datasource to run this expression against. If omitted, the expression will be run against the `__expr__` datasource */ + datasourceUID?: string; model: any; - queryType: string; + /** The type of query if this is a query expression */ + queryType?: string; relativeTimeRange?: { from: string; to: string; }; + /** Used to mark the expression to be used as the final source for the rule evaluation + Only one expression in a rule can be marked as the source + For AlertRules, this is the expression that will be evaluated against the alerting condition + For RecordingRules, this is the expression that will be recorded */ source?: boolean; }; }; From 3081ac166adcfe64dfd8e77e1b1c0038dbb4950a Mon Sep 17 00:00:00 2001 From: Andreas Christou Date: Sat, 13 Sep 2025 00:23:44 +0200 Subject: [PATCH 13/33] Graphite: Backend functions endpoint (#110771) * Add lint rules * Backend decoupling - Add standalone files - Add graphite query type - Add logger to Service - Create logger in the ProvideService method - Use a pointer for the HTTP client provider - Update logger usage everywhere - Update tracer type - Replace simplejson with json - Add dummy CallResource and CheckHealth methods - Update tests * Update ConfigEditor imports * Update types imports * Update datasource - Switch to using semver package - Update imports * Update store imports * Update helper imports and notification creation * Update context import * Update version numbers and logic * Copy array_move from core * Test updates * Add required files and update plugin.json * Update core references and packages * Remove commented code * Update wire * Lint * Fix import * Copy null type * More lint * Update snapshot * Refactor backend - Split query logic into separate file - Move utils to separate file * Add health-check logic - Support backend healthcheck if the FF is enabled * Remove query import support as unneeded * Add test * Add util function for decoding responses * Add events types * Add resource handler * Add events handler and generic resource req handler * Tests * Update frontend - Add types - Update events function to support backend requests * Lint and typing * Lint * Add metrics find endpoint - Add types - Add generic response parser - Add endpoint - Tests * Update FE functoin to use backend endpoint * Lint * Simplify request * Update test * Metrics expand type * Extract shared logic and add metric expand endpoint * Update tests * Call metric expand from backend * Rename type for clarity * Add get resource req handler * Refactor doGraphiteRequest, parseResponse Update tests * Migrate functions endpoint to backend * Add tests * Review * Review * Fix packages * Format * Fix merge issues * Review * Fix undefined values * Extract request creation - Add method for create requests generically with tests - Replace usage in query method - Update usages in resource handlers - Update tests - Update types * Lint * Lint --- pkg/tsdb/graphite/resource_handler.go | 106 +++++++++----- pkg/tsdb/graphite/resource_handler_test.go | 131 ++++++++++++++++-- .../plugins/datasource/graphite/datasource.ts | 8 +- 3 files changed, 195 insertions(+), 50 deletions(-) diff --git a/pkg/tsdb/graphite/resource_handler.go b/pkg/tsdb/graphite/resource_handler.go index 6988aafaad3..99199c4de75 100644 --- a/pkg/tsdb/graphite/resource_handler.go +++ b/pkg/tsdb/graphite/resource_handler.go @@ -1,6 +1,7 @@ package graphite import ( + "bytes" "context" "encoding/json" "fmt" @@ -16,13 +17,14 @@ import ( "go.opentelemetry.io/otel/codes" ) -type resourceHandler[T any] func(context.Context, *datasourceInfo, T) ([]byte, int, error) +type resourceHandler[T any] func(context.Context, *datasourceInfo, *T) ([]byte, int, error) func (s *Service) newResourceMux() *http.ServeMux { mux := http.NewServeMux() - mux.HandleFunc("/events", handleResourceReq[GraphiteEventsRequest](s.handleEvents, s)) - mux.HandleFunc("/metrics/find", handleResourceReq[GraphiteMetricsFindRequest](s.handleMetricsFind, s)) - mux.HandleFunc("/metrics/expand", handleResourceReq[GraphiteMetricsFindRequest](s.handleMetricsExpand, s)) + mux.HandleFunc("/events", handleResourceReq(s.handleEvents, s)) + mux.HandleFunc("/metrics/find", handleResourceReq(s.handleMetricsFind, s)) + mux.HandleFunc("/metrics/expand", handleResourceReq(s.handleMetricsExpand, s)) + mux.HandleFunc("/functions", handleResourceReq(s.handleFunctions, s)) return mux } @@ -39,17 +41,28 @@ func handleResourceReq[T any](handlerFn resourceHandler[T], s *Service) func(rw } defer func() { - if err := req.Body.Close(); err != nil { - s.logger.Warn("Failed to close response body", "err", err) + if req.Body != nil { + if err := req.Body.Close(); err != nil { + s.logger.Warn("Failed to close request body", "err", err) + writeErrorResponse(rw, http.StatusInternalServerError, fmt.Sprintf("unexpected error %v", err)) + return + } + } + }() + + var parsedBody *T + if req.Body != nil { + body, err := io.ReadAll(req.Body) + if err != nil { + s.logger.Error("Failed to read request body", "error", err) writeErrorResponse(rw, http.StatusInternalServerError, fmt.Sprintf("unexpected error %v", err)) return } - }() - requestBody, err := io.ReadAll(req.Body) - if err != nil { - s.logger.Error("Failed to read request body", "error", err) - writeErrorResponse(rw, http.StatusInternalServerError, fmt.Sprintf("unexpected error %v", err)) - return + parsedBody, err = parseRequestBody[T](body, s.logger) + if err != nil { + writeErrorResponse(rw, http.StatusBadRequest, fmt.Sprintf("failed to parse request body: %v", err)) + return + } } if handlerFn == nil { @@ -57,13 +70,7 @@ func handleResourceReq[T any](handlerFn resourceHandler[T], s *Service) func(rw return } - parsedBody, err := parseRequestBody[T](requestBody, s.logger) - if err != nil { - writeErrorResponse(rw, http.StatusBadRequest, fmt.Sprintf("failed to parse request body: %v", err)) - return - } - - response, statusCode, err := handlerFn(ctx, dsInfo, *parsedBody) + response, statusCode, err := handlerFn(ctx, dsInfo, parsedBody) if err != nil { writeErrorResponse(rw, statusCode, fmt.Sprintf("failed to handle resource request: %v", err)) return @@ -78,7 +85,7 @@ func handleResourceReq[T any](handlerFn resourceHandler[T], s *Service) func(rw } } -func (s *Service) handleEvents(ctx context.Context, dsInfo *datasourceInfo, eventsRequestJson GraphiteEventsRequest) ([]byte, int, error) { +func (s *Service) handleEvents(ctx context.Context, dsInfo *datasourceInfo, eventsRequestJson *GraphiteEventsRequest) ([]byte, int, error) { queryParams := map[string]string{ "from": eventsRequestJson.From, "until": eventsRequestJson.Until, @@ -96,7 +103,7 @@ func (s *Service) handleEvents(ctx context.Context, dsInfo *datasourceInfo, even return nil, http.StatusInternalServerError, fmt.Errorf("failed to create events request %v", err) } - events, statusCode, err := doGraphiteRequest[[]GraphiteEventsResponse](ctx, dsInfo, s.logger, req) + events, _, statusCode, err := doGraphiteRequest[[]GraphiteEventsResponse](ctx, dsInfo, s.logger, req, false) if err != nil { return nil, statusCode, fmt.Errorf("events request failed: %v", err) } @@ -112,7 +119,7 @@ func (s *Service) handleEvents(ctx context.Context, dsInfo *datasourceInfo, even return graphiteEventsResponse, statusCode, nil } -func (s *Service) handleMetricsFind(ctx context.Context, dsInfo *datasourceInfo, metricsFindRequestJson GraphiteMetricsFindRequest) ([]byte, int, error) { +func (s *Service) handleMetricsFind(ctx context.Context, dsInfo *datasourceInfo, metricsFindRequestJson *GraphiteMetricsFindRequest) ([]byte, int, error) { if metricsFindRequestJson.Query == "" { return nil, http.StatusBadRequest, fmt.Errorf("query is required") } @@ -139,7 +146,7 @@ func (s *Service) handleMetricsFind(ctx context.Context, dsInfo *datasourceInfo, return nil, http.StatusInternalServerError, fmt.Errorf("failed to create metrics find request %v", err) } - metrics, statusCode, err := doGraphiteRequest[[]GraphiteMetricsFindResponse](ctx, dsInfo, s.logger, req) + metrics, _, statusCode, err := doGraphiteRequest[[]GraphiteMetricsFindResponse](ctx, dsInfo, s.logger, req, false) if err != nil { return nil, statusCode, fmt.Errorf("metrics find request failed: %v", err) } @@ -152,7 +159,7 @@ func (s *Service) handleMetricsFind(ctx context.Context, dsInfo *datasourceInfo, return metricsFindResponse, statusCode, nil } -func (s *Service) handleMetricsExpand(ctx context.Context, dsInfo *datasourceInfo, metricsExpandRequestJson GraphiteMetricsFindRequest) ([]byte, int, error) { +func (s *Service) handleMetricsExpand(ctx context.Context, dsInfo *datasourceInfo, metricsExpandRequestJson *GraphiteMetricsFindRequest) ([]byte, int, error) { if metricsExpandRequestJson.Query == "" { return nil, http.StatusBadRequest, fmt.Errorf("query is required") } @@ -176,7 +183,7 @@ func (s *Service) handleMetricsExpand(ctx context.Context, dsInfo *datasourceInf return nil, http.StatusInternalServerError, fmt.Errorf("failed to create metrics expand request %v", err) } - metrics, statusCode, err := doGraphiteRequest[GraphiteMetricsExpandResponse](ctx, dsInfo, s.logger, req) + metrics, _, statusCode, err := doGraphiteRequest[GraphiteMetricsExpandResponse](ctx, dsInfo, s.logger, req, false) if err != nil { return nil, statusCode, fmt.Errorf("metrics expand request failed: %v", err) } @@ -196,7 +203,29 @@ func (s *Service) handleMetricsExpand(ctx context.Context, dsInfo *datasourceInf return metricsExpandResponse, statusCode, nil } -func doGraphiteRequest[T any](ctx context.Context, dsInfo *datasourceInfo, logger log.Logger, req *http.Request) (*T, int, error) { +func (s *Service) handleFunctions(ctx context.Context, dsInfo *datasourceInfo, _ *any) ([]byte, int, error) { + req, err := s.createRequest(ctx, dsInfo, URLParams{ + SubPath: "functions", + Method: http.MethodGet, + }) + if err != nil { + return nil, http.StatusInternalServerError, fmt.Errorf("failed to create functions request %v", err) + } + + _, rawBody, statusCode, err := doGraphiteRequest[map[string]any](ctx, dsInfo, s.logger, req, true) + if err != nil { + return nil, statusCode, fmt.Errorf("version request failed: %v", err) + } + + if rawBody == nil { + return []byte{}, statusCode, nil + } + + rawBodyReplaced := bytes.ReplaceAll(*rawBody, []byte("\"default\": Infinity"), []byte("\"default\": 1e9999")) + return rawBodyReplaced, statusCode, nil +} + +func doGraphiteRequest[T any](ctx context.Context, dsInfo *datasourceInfo, logger log.Logger, req *http.Request, isRaw bool) (*T, *[]byte, int, error) { _, span := tracing.DefaultTracer().Start(ctx, "graphite request") defer span.End() span.SetAttributes( @@ -209,7 +238,7 @@ func doGraphiteRequest[T any](ctx context.Context, dsInfo *datasourceInfo, logge if err != nil { span.RecordError(err) span.SetStatus(codes.Error, err.Error()) - return nil, http.StatusInternalServerError, fmt.Errorf("failed to complete request: %v", err) + return nil, nil, http.StatusInternalServerError, fmt.Errorf("failed to complete request: %v", err) } defer func() { @@ -218,12 +247,12 @@ func doGraphiteRequest[T any](ctx context.Context, dsInfo *datasourceInfo, logge } }() - parsedResponse, err := parseResponse[T](res) + parsedResponse, rawBody, err := parseResponse[T](res, isRaw, logger) if err != nil { - return nil, http.StatusInternalServerError, fmt.Errorf("failed to parse response: %v", err) + return nil, nil, http.StatusInternalServerError, fmt.Errorf("failed to parse response: %v", err) } - return parsedResponse, res.StatusCode, nil + return parsedResponse, rawBody, res.StatusCode, nil } func parseRequestBody[V any](requestBody []byte, logger log.Logger) (*V, error) { @@ -236,19 +265,28 @@ func parseRequestBody[V any](requestBody []byte, logger log.Logger) (*V, error) return requestJson, nil } -func parseResponse[V any](res *http.Response) (*V, error) { +func parseResponse[V any](res *http.Response, isRaw bool, logger log.Logger) (*V, *[]byte, error) { encoding := res.Header.Get("Content-Encoding") body, err := decode(encoding, res.Body) if err != nil { - return nil, fmt.Errorf("failed to read response: %v", err) + return nil, nil, fmt.Errorf("failed to read response: %v", err) + } + + if res.StatusCode/100 != 2 { + logger.Warn("Request failed", "status", res.Status, "body", string(body)) + return nil, nil, fmt.Errorf("request failed, status: %d", res.StatusCode) + } + + if isRaw { + return nil, &body, nil } data := new(V) err = json.Unmarshal(body, &data) if err != nil { - return nil, fmt.Errorf("failed to unmarshal response: %v", err) + return nil, nil, fmt.Errorf("failed to unmarshal response: %v", err) } - return data, nil + return data, nil, nil } func writeErrorResponse(rw http.ResponseWriter, code int, msg string) { diff --git a/pkg/tsdb/graphite/resource_handler_test.go b/pkg/tsdb/graphite/resource_handler_test.go index 51d5a0fb290..4352b34cc79 100644 --- a/pkg/tsdb/graphite/resource_handler_test.go +++ b/pkg/tsdb/graphite/resource_handler_test.go @@ -18,12 +18,14 @@ import ( ) type mockRoundTripper struct { - respBody []byte - status int - err error + respBody []byte + status int + err error + lastRequest *http.Request } func (m *mockRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + m.lastRequest = req if m.err != nil { return nil, m.err } @@ -129,7 +131,7 @@ func TestHandleEvents(t *testing.T) { t.Run(tt.name, func(t *testing.T) { svc := &Service{logger: log.NewNullLogger()} - respBody, status, err := svc.handleEvents(context.Background(), tt.dsInfo, tt.request) + respBody, status, err := svc.handleEvents(context.Background(), tt.dsInfo, &tt.request) assert.Equal(t, tt.expectedStatus, status) @@ -234,7 +236,7 @@ func TestHandleMetricsFind(t *testing.T) { t.Run(tt.name, func(t *testing.T) { svc := &Service{logger: log.NewNullLogger()} - respBody, status, err := svc.handleMetricsFind(context.Background(), tt.dsInfo, tt.request) + respBody, status, err := svc.handleMetricsFind(context.Background(), tt.dsInfo, &tt.request) assert.Equal(t, tt.expectedStatus, status) @@ -368,7 +370,7 @@ func TestHandleMetricsExpand(t *testing.T) { t.Run(tt.name, func(t *testing.T) { svc := &Service{logger: log.NewNullLogger()} - respBody, status, err := svc.handleMetricsExpand(context.Background(), tt.dsInfo, tt.request) + respBody, status, err := svc.handleMetricsExpand(context.Background(), tt.dsInfo, &tt.request) assert.Equal(t, tt.expectedStatus, status) @@ -392,6 +394,106 @@ func TestHandleMetricsExpand(t *testing.T) { } } +func TestHandleFunctions(t *testing.T) { + tests := []struct { + name string + responseBody string + statusCode int + expectError bool + errorContains string + expectedData string + }{ + { + name: "successful functions request", + responseBody: `{"sum": {"description": "Sum function"}, "avg": {"description": "Average function"}}`, + statusCode: 200, + expectError: false, + expectedData: `{"sum": {"description": "Sum function"}, "avg": {"description": "Average function"}}`, + }, + { + name: "functions with infinity replacement", + responseBody: `{"func": {"default": Infinity, "description": "Test function"}}`, + statusCode: 200, + expectError: false, + expectedData: `{"func": {"default": 1e9999, "description": "Test function"}}`, + }, + { + name: "empty functions response", + responseBody: `{}`, + statusCode: 200, + expectError: false, + expectedData: `{}`, + }, + { + name: "functions request server error", + responseBody: `{"error": "internal error"}`, + statusCode: 500, + expectError: true, + errorContains: "version request failed", + }, + { + name: "functions request not found", + responseBody: `{"error": "not found"}`, + statusCode: 404, + expectError: true, + errorContains: "version request failed", + }, + { + name: "network error", + responseBody: "", + statusCode: 0, + expectError: true, + errorContains: "version request failed", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var mockTransport *mockRoundTripper + + if tt.name == "network error" { + mockTransport = &mockRoundTripper{ + err: errors.New("network connection failed"), + } + } else { + mockTransport = &mockRoundTripper{ + respBody: []byte(tt.responseBody), + status: tt.statusCode, + } + } + + dsInfo := &datasourceInfo{ + HTTPClient: &http.Client{Transport: mockTransport}, + URL: "http://graphite.example.com", + } + + service := &Service{ + logger: log.NewNullLogger(), + } + + result, statusCode, err := service.handleFunctions(context.Background(), dsInfo, nil) + + if tt.expectError { + assert.Error(t, err) + if tt.errorContains != "" { + assert.Contains(t, err.Error(), tt.errorContains) + } + } else { + assert.NoError(t, err) + assert.Equal(t, tt.statusCode, statusCode) + assert.Equal(t, tt.expectedData, string(result)) + } + + // Verify the request was made correctly (except for network error case) + if tt.name != "network error" { + require.NotNil(t, mockTransport.lastRequest) + assert.Equal(t, "http://graphite.example.com/functions", mockTransport.lastRequest.URL.String()) + assert.Equal(t, http.MethodGet, mockTransport.lastRequest.Method) + } + }) + } +} + func TestHandleResourceReq_Success(t *testing.T) { mockEvents := []GraphiteEventsResponse{{When: 1234567890, What: "event1"}} mockResp, _ := json.Marshal(mockEvents) @@ -558,11 +660,10 @@ func TestDoGraphiteRequest(t *testing.T) { URL: "http://graphite.grafana", HTTPClient: &http.Client{Transport: &mockRoundTripper{respBody: []byte("[]"), status: 500}}, }, - method: "GET", - headers: map[string]string{}, - expectedStatus: 500, - expectError: false, - expectedData: []GraphiteEventsResponse{}, + method: "GET", + headers: map[string]string{}, + expectError: true, + errorContains: "request failed, status: 500", }, } @@ -594,7 +695,7 @@ func TestDoGraphiteRequest(t *testing.T) { assert.NoError(t, err) } - result, status, err := doGraphiteRequest[[]GraphiteEventsResponse](ctx, tt.dsInfo, svc.logger, req) + result, _, status, err := doGraphiteRequest[[]GraphiteEventsResponse](ctx, tt.dsInfo, svc.logger, req, false) if tt.expectError { assert.Error(t, err) @@ -653,7 +754,7 @@ func TestDoGraphiteRequestGenericTypes(t *testing.T) { }) assert.NoError(t, err) - result, status, err := doGraphiteRequest[[]GraphiteMetricsFindResponse](ctx, dsInfo, svc.logger, req) + result, _, status, err := doGraphiteRequest[[]GraphiteMetricsFindResponse](ctx, dsInfo, svc.logger, req, false) assert.NoError(t, err) assert.NotNil(t, result) @@ -681,7 +782,7 @@ func TestDoGraphiteRequestGenericTypes(t *testing.T) { }) assert.NoError(t, err) - result, status, err := doGraphiteRequest[GraphiteMetricsExpandResponse](ctx, dsInfo, svc.logger, req) + result, _, status, err := doGraphiteRequest[GraphiteMetricsExpandResponse](ctx, dsInfo, svc.logger, req, false) assert.NoError(t, err) assert.NotNil(t, result) @@ -825,7 +926,7 @@ func TestParseResponse(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result, err := parseResponse[[]GraphiteEventsResponse](tt.response) + result, _, err := parseResponse[[]GraphiteEventsResponse](tt.response, false, log.NewNullLogger()) if tt.expectError { assert.Error(t, err) diff --git a/public/app/plugins/datasource/graphite/datasource.ts b/public/app/plugins/datasource/graphite/datasource.ts index 7f04fa713af..fc2e818233f 100644 --- a/public/app/plugins/datasource/graphite/datasource.ts +++ b/public/app/plugins/datasource/graphite/datasource.ts @@ -947,7 +947,7 @@ export class GraphiteDatasource return this.getFuncDefs(); } - getFuncDefs() { + async getFuncDefs() { if (this.funcDefsPromise !== null) { return this.funcDefsPromise; } @@ -966,6 +966,12 @@ export class GraphiteDatasource responseType: 'text' as const, }; + if (config.featureToggles.graphiteBackendMode) { + const functions = await this.getResource('functions'); + this.funcDefs = gfunc.parseFuncDefs(functions); + return this.funcDefs; + } + return lastValueFrom( this.doGraphiteRequest(httpOptions).pipe( map((results: FetchResponse) => { From 211c0ca5c372bb26b152e2efaf22c489d0fd0948 Mon Sep 17 00:00:00 2001 From: Andreas Christou Date: Sat, 13 Sep 2025 00:53:09 +0200 Subject: [PATCH 14/33] Graphite: Backend tags autocomplete endpoint (#110772) * Add lint rules * Backend decoupling - Add standalone files - Add graphite query type - Add logger to Service - Create logger in the ProvideService method - Use a pointer for the HTTP client provider - Update logger usage everywhere - Update tracer type - Replace simplejson with json - Add dummy CallResource and CheckHealth methods - Update tests * Update ConfigEditor imports * Update types imports * Update datasource - Switch to using semver package - Update imports * Update store imports * Update helper imports and notification creation * Update context import * Update version numbers and logic * Copy array_move from core * Test updates * Add required files and update plugin.json * Update core references and packages * Remove commented code * Update wire * Lint * Fix import * Copy null type * More lint * Update snapshot * Refactor backend - Split query logic into separate file - Move utils to separate file * Add health-check logic - Support backend healthcheck if the FF is enabled * Remove query import support as unneeded * Add test * Add util function for decoding responses * Add events types * Add resource handler * Add events handler and generic resource req handler * Tests * Update frontend - Add types - Update events function to support backend requests * Lint and typing * Lint * Add metrics find endpoint - Add types - Add generic response parser - Add endpoint - Tests * Update FE functoin to use backend endpoint * Lint * Simplify request * Update test * Metrics expand type * Extract shared logic and add metric expand endpoint * Update tests * Call metric expand from backend * Rename type for clarity * Add get resource req handler * Refactor doGraphiteRequest, parseResponse Update tests * Migrate functions endpoint to backend * Support tags autocomplete in backend - Add tests - Add types - Remove unneeded comments * Add tests * Review * Review * Fix packages * Format * Fix merge issues * Review * Fix undefined values * Extract request creation - Add method for create requests generically with tests - Replace usage in query method - Update usages in resource handlers - Update tests - Update types --- pkg/tsdb/graphite/resource_handler.go | 30 +++++ pkg/tsdb/graphite/resource_handler_test.go | 110 +++++++++++++++++- pkg/tsdb/graphite/types.go | 7 ++ .../plugins/datasource/graphite/datasource.ts | 14 ++- 4 files changed, 157 insertions(+), 4 deletions(-) diff --git a/pkg/tsdb/graphite/resource_handler.go b/pkg/tsdb/graphite/resource_handler.go index 99199c4de75..d8a1c3508ec 100644 --- a/pkg/tsdb/graphite/resource_handler.go +++ b/pkg/tsdb/graphite/resource_handler.go @@ -25,6 +25,7 @@ func (s *Service) newResourceMux() *http.ServeMux { mux.HandleFunc("/metrics/find", handleResourceReq(s.handleMetricsFind, s)) mux.HandleFunc("/metrics/expand", handleResourceReq(s.handleMetricsExpand, s)) mux.HandleFunc("/functions", handleResourceReq(s.handleFunctions, s)) + mux.HandleFunc("/tags/autoComplete/tags", handleResourceReq(s.handleTagsAutocomplete, s)) return mux } @@ -203,6 +204,35 @@ func (s *Service) handleMetricsExpand(ctx context.Context, dsInfo *datasourceInf return metricsExpandResponse, statusCode, nil } +func (s *Service) handleTagsAutocomplete(ctx context.Context, dsInfo *datasourceInfo, tagsAutocompleteRequestJson *GraphiteTagsRequest) ([]byte, int, error) { + queryParams := map[string]string{ + "from": tagsAutocompleteRequestJson.From, + "until": tagsAutocompleteRequestJson.Until, + "limit": fmt.Sprintf("%d", tagsAutocompleteRequestJson.Limit), + "tagPrefix": tagsAutocompleteRequestJson.TagPrefix, + } + req, err := s.createRequest(ctx, dsInfo, URLParams{ + SubPath: "tags/autoComplete/tags", + Method: http.MethodGet, + QueryParams: queryParams, + }) + if err != nil { + return nil, http.StatusInternalServerError, fmt.Errorf("failed to create metrics expand request %v", err) + } + + tags, _, statusCode, err := doGraphiteRequest[[]string](ctx, dsInfo, s.logger, req, false) + if err != nil { + return nil, statusCode, fmt.Errorf("tags autocomplete request failed: %v", err) + } + + tagsResponse, err := json.Marshal(tags) + if err != nil { + return nil, http.StatusInternalServerError, fmt.Errorf("failed to marshal tags autocomplete response: %s", err) + } + + return tagsResponse, statusCode, nil +} + func (s *Service) handleFunctions(ctx context.Context, dsInfo *datasourceInfo, _ *any) ([]byte, int, error) { req, err := s.createRequest(ctx, dsInfo, URLParams{ SubPath: "functions", diff --git a/pkg/tsdb/graphite/resource_handler_test.go b/pkg/tsdb/graphite/resource_handler_test.go index 4352b34cc79..c331c2d2cab 100644 --- a/pkg/tsdb/graphite/resource_handler_test.go +++ b/pkg/tsdb/graphite/resource_handler_test.go @@ -5,6 +5,7 @@ import ( "context" "encoding/json" "errors" + "fmt" "io" "net/http" "net/http/httptest" @@ -94,7 +95,7 @@ func TestHandleEvents(t *testing.T) { name: "Invalid URL", dsInfo: &datasourceInfo{ Id: 1, - URL: "ht tp://invalid url", // Invalid URL + URL: "ht tp://invalid url", }, request: GraphiteEventsRequest{From: "now-1h", Until: "now"}, expectedStatus: http.StatusInternalServerError, @@ -211,7 +212,7 @@ func TestHandleMetricsFind(t *testing.T) { name: "Invalid URL", dsInfo: &datasourceInfo{ Id: 1, - URL: "ht tp://invalid url", // Invalid URL + URL: "ht tp://invalid url", }, request: GraphiteMetricsFindRequest{Query: "app.grafana.*"}, expectedStatus: http.StatusInternalServerError, @@ -321,7 +322,7 @@ func TestHandleMetricsExpand(t *testing.T) { name: "Invalid URL", dsInfo: &datasourceInfo{ Id: 1, - URL: "ht tp://invalid url", // Invalid URL + URL: "ht tp://invalid url", }, request: GraphiteMetricsFindRequest{Query: "app.grafana.*"}, expectedStatus: http.StatusInternalServerError, @@ -394,6 +395,109 @@ func TestHandleMetricsExpand(t *testing.T) { } } +func TestHandleTagsAutocomplete(t *testing.T) { + tests := []struct { + name string + request GraphiteTagsRequest + responseBody string + statusCode int + expectError bool + errorContains string + expectedData []string + }{ + { + name: "successful tags autocomplete request", + request: GraphiteTagsRequest{ + From: "1h", + Until: "now", + Limit: 10, + TagPrefix: "app", + }, + responseBody: `["app", "application", "app_name"]`, + statusCode: 200, + expectedData: []string{"app", "application", "app_name"}, + }, + { + name: "tags autocomplete with minimal request", + request: GraphiteTagsRequest{}, + responseBody: `["tag1", "tag2"]`, + statusCode: 200, + expectedData: []string{"tag1", "tag2"}, + }, + { + name: "tags autocomplete with empty response", + request: GraphiteTagsRequest{ + TagPrefix: "nonexistent", + }, + responseBody: `[]`, + statusCode: 200, + expectedData: []string{}, + }, + { + name: "tags autocomplete server error - invalid JSON causes marshal error", + request: GraphiteTagsRequest{ + From: "invalid", + }, + responseBody: `invalid json response`, + statusCode: 400, + expectError: true, + errorContains: "tags autocomplete request failed", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockTransport := &mockRoundTripper{ + respBody: []byte(tt.responseBody), + status: tt.statusCode, + } + + dsInfo := &datasourceInfo{ + HTTPClient: &http.Client{Transport: mockTransport}, + URL: "http://graphite.example.com", + } + + service := &Service{ + logger: log.NewNullLogger(), + } + + result, statusCode, err := service.handleTagsAutocomplete(context.Background(), dsInfo, &tt.request) + + if tt.expectError { + assert.Error(t, err) + if tt.errorContains != "" { + assert.Contains(t, err.Error(), tt.errorContains) + } + } else { + assert.NoError(t, err) + assert.Equal(t, tt.statusCode, statusCode) + + var tags []string + err = json.Unmarshal(result, &tags) + assert.NoError(t, err) + assert.Equal(t, tt.expectedData, tags) + } + + if !tt.expectError { + expectedURL := "http://graphite.example.com/tags/autoComplete/tags" + assert.Contains(t, mockTransport.lastRequest.URL.String(), expectedURL) + + if tt.request.From != "" { + assert.Contains(t, mockTransport.lastRequest.URL.RawQuery, fmt.Sprintf("from=%s", tt.request.From)) + } + if tt.request.Until != "" { + assert.Contains(t, mockTransport.lastRequest.URL.RawQuery, fmt.Sprintf("until=%s", tt.request.Until)) + } + if tt.request.Limit != 0 { + assert.Contains(t, mockTransport.lastRequest.URL.RawQuery, fmt.Sprintf("limit=%d", tt.request.Limit)) + } + if tt.request.TagPrefix != "" { + assert.Contains(t, mockTransport.lastRequest.URL.RawQuery, fmt.Sprintf("tagPrefix=%s", tt.request.TagPrefix)) + } + } + }) + } +} func TestHandleFunctions(t *testing.T) { tests := []struct { name string diff --git a/pkg/tsdb/graphite/types.go b/pkg/tsdb/graphite/types.go index 30d872f375a..2e427f1d327 100644 --- a/pkg/tsdb/graphite/types.go +++ b/pkg/tsdb/graphite/types.go @@ -59,3 +59,10 @@ type GraphiteMetricsFindResponse struct { type GraphiteMetricsExpandResponse struct { Results []string `json:"results"` } + +type GraphiteTagsRequest struct { + From string `json:"from"` + Until string `json:"until"` + Limit int `json:"limit,omitempty"` + TagPrefix string `json:"tagPrefix,omitempty"` +} diff --git a/public/app/plugins/datasource/graphite/datasource.ts b/public/app/plugins/datasource/graphite/datasource.ts index fc2e818233f..14b1139f3ad 100644 --- a/public/app/plugins/datasource/graphite/datasource.ts +++ b/public/app/plugins/datasource/graphite/datasource.ts @@ -854,7 +854,7 @@ export class GraphiteDatasource ); } - getTagsAutoComplete(expressions: string[], tagPrefix?: string, optionalOptions?: any) { + async getTagsAutoComplete(expressions: string[], tagPrefix?: string, optionalOptions?: any) { const options = optionalOptions || {}; const params: BackendSrvRequest['params'] = { expr: _map(expressions, (expression) => this.templateSrv.replace((expression || '').trim())), @@ -871,6 +871,18 @@ export class GraphiteDatasource params.until = this.translateTime(options.range.to, true, options.timezone); } + if (config.featureToggles.graphiteBackendMode) { + const tags = await this.postResource('tags/autoComplete/tags', { + from: typeof params.from === 'string' ? params.from : `${params.from}`, + until: typeof params.until === 'string' ? params.until : `${params.until}`, + tagPrefix, + limit: options.limit, + }); + return tags.map((tag) => ({ + text: tag, + })); + } + const httpOptions: BackendSrvRequest = { method: 'GET', url: '/tags/autoComplete/tags', From 135e9ef1024345e8c7928ab6a59c656f8513235d Mon Sep 17 00:00:00 2001 From: Ezequiel Victorero Date: Fri, 12 Sep 2025 20:23:50 -0300 Subject: [PATCH 15/33] ShortURL: Use the new k8s api in the frontend (#110537) --- .../src/types/featureToggles.gen.ts | 6 +- pkg/services/featuremgmt/registry.go | 9 +- pkg/services/featuremgmt/toggles_gen.csv | 1 + pkg/services/featuremgmt/toggles_gen.go | 6 +- pkg/services/featuremgmt/toggles_gen.json | 25 +- .../shorturl.grafana.app-v1alpha1.json | 1823 +++++++++++++++++ pkg/tests/apis/openapi_test.go | 4 + .../api/clients/shorturl/v1alpha1/baseAPI.ts | 14 + .../shorturl/v1alpha1/endpoints.gen.ts | 582 ++++++ .../api/clients/shorturl/v1alpha1/index.ts | 3 + public/app/core/reducers/root.ts | 2 + public/app/core/utils/shortLinks.test.ts | 79 +- public/app/core/utils/shortLinks.ts | 30 +- public/app/store/configureStore.ts | 2 + scripts/generate-rtk-apis.ts | 5 + 15 files changed, 2573 insertions(+), 18 deletions(-) create mode 100644 pkg/tests/apis/openapi_snapshots/shorturl.grafana.app-v1alpha1.json create mode 100644 public/app/api/clients/shorturl/v1alpha1/baseAPI.ts create mode 100644 public/app/api/clients/shorturl/v1alpha1/endpoints.gen.ts create mode 100644 public/app/api/clients/shorturl/v1alpha1/index.ts diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index b98349e852d..1da6c2612cc 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -274,10 +274,14 @@ export interface FeatureToggles { */ kubernetesDashboards?: boolean; /** - * Routes short url requests from /api to the /apis endpoint + * Enables k8s short url api and uses it under the hood when handling legacy /api */ kubernetesShortURLs?: boolean; /** + * Routes short url requests from /api to the /apis endpoint in the frontend. Depends on kubernetesShortURLs + */ + useKubernetesShortURLsAPI?: boolean; + /** * Adds support for Kubernetes alerting and recording rules */ kubernetesAlertingRules?: boolean; diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index a19ae8f3f96..e347dcf9c1e 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -457,11 +457,18 @@ var ( }, { Name: "kubernetesShortURLs", - Description: "Routes short url requests from /api to the /apis endpoint", + Description: "Enables k8s short url api and uses it under the hood when handling legacy /api", Stage: FeatureStageExperimental, Owner: grafanaAppPlatformSquad, RequiresRestart: true, // changes the API routing }, + { + Name: "useKubernetesShortURLsAPI", + Description: "Routes short url requests from /api to the /apis endpoint in the frontend. Depends on kubernetesShortURLs", + Stage: FeatureStageExperimental, + Owner: grafanaSharingSquad, + FrontendOnly: true, + }, { Name: "kubernetesAlertingRules", Description: "Adds support for Kubernetes alerting and recording rules", diff --git a/pkg/services/featuremgmt/toggles_gen.csv b/pkg/services/featuremgmt/toggles_gen.csv index 0294b58ccf3..74b4de46251 100644 --- a/pkg/services/featuremgmt/toggles_gen.csv +++ b/pkg/services/featuremgmt/toggles_gen.csv @@ -59,6 +59,7 @@ kubernetesSnapshots,experimental,@grafana/grafana-app-platform-squad,false,true, kubernetesLibraryPanels,experimental,@grafana/grafana-app-platform-squad,false,true,false kubernetesDashboards,GA,@grafana/dashboards-squad,false,false,true kubernetesShortURLs,experimental,@grafana/grafana-app-platform-squad,false,true,false +useKubernetesShortURLsAPI,experimental,@grafana/sharing-squad,false,false,true kubernetesAlertingRules,experimental,@grafana/alerting-squad,false,true,false dashboardDisableSchemaValidationV1,experimental,@grafana/grafana-app-platform-squad,false,false,false dashboardDisableSchemaValidationV2,experimental,@grafana/grafana-app-platform-squad,false,false,false diff --git a/pkg/services/featuremgmt/toggles_gen.go b/pkg/services/featuremgmt/toggles_gen.go index f337c991e09..5376d303c15 100644 --- a/pkg/services/featuremgmt/toggles_gen.go +++ b/pkg/services/featuremgmt/toggles_gen.go @@ -244,9 +244,13 @@ const ( FlagKubernetesDashboards = "kubernetesDashboards" // FlagKubernetesShortURLs - // Routes short url requests from /api to the /apis endpoint + // Enables k8s short url api and uses it under the hood when handling legacy /api FlagKubernetesShortURLs = "kubernetesShortURLs" + // FlagUseKubernetesShortURLsAPI + // Routes short url requests from /api to the /apis endpoint in the frontend. Depends on kubernetesShortURLs + FlagUseKubernetesShortURLsAPI = "useKubernetesShortURLsAPI" + // FlagKubernetesAlertingRules // Adds support for Kubernetes alerting and recording rules FlagKubernetesAlertingRules = "kubernetesAlertingRules" diff --git a/pkg/services/featuremgmt/toggles_gen.json b/pkg/services/featuremgmt/toggles_gen.json index 9337a5f30e7..24f00874702 100644 --- a/pkg/services/featuremgmt/toggles_gen.json +++ b/pkg/services/featuremgmt/toggles_gen.json @@ -2048,11 +2048,14 @@ { "metadata": { "name": "kubernetesShortURLs", - "resourceVersion": "1753722806283", - "creationTimestamp": "2025-08-04T12:12:12Z" + "resourceVersion": "1756914263808", + "creationTimestamp": "2025-08-04T12:12:12Z", + "annotations": { + "grafana.app/updatedTimestamp": "2025-09-03 15:44:23.80856 +0000 UTC" + } }, "spec": { - "description": "Routes short url requests from /api to the /apis endpoint", + "description": "Enables k8s short url api and uses it under the hood when handling legacy /api", "stage": "experimental", "codeowner": "@grafana/grafana-app-platform-squad", "requiresRestart": true @@ -3625,6 +3628,22 @@ "hideFromDocs": true } }, + { + "metadata": { + "name": "useKubernetesShortURLsAPI", + "resourceVersion": "1756914263808", + "creationTimestamp": "2025-09-03T10:49:07Z", + "annotations": { + "grafana.app/updatedTimestamp": "2025-09-03 15:44:23.80856 +0000 UTC" + } + }, + "spec": { + "description": "Routes short url requests from /api to the /apis endpoint in the frontend. Depends on kubernetesShortURLs", + "stage": "experimental", + "codeowner": "@grafana/sharing-squad", + "frontend": true + } + }, { "metadata": { "name": "useScopeSingleNodeEndpoint", diff --git a/pkg/tests/apis/openapi_snapshots/shorturl.grafana.app-v1alpha1.json b/pkg/tests/apis/openapi_snapshots/shorturl.grafana.app-v1alpha1.json new file mode 100644 index 00000000000..9d90de21524 --- /dev/null +++ b/pkg/tests/apis/openapi_snapshots/shorturl.grafana.app-v1alpha1.json @@ -0,0 +1,1823 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "shorturl.grafana.app/v1alpha1" + }, + "paths": { + "/apis/shorturl.grafana.app/v1alpha1/": { + "get": { + "tags": [ + "API Discovery" + ], + "description": "Describe the available kubernetes resources", + "operationId": "getAPIResources", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" + } + } + } + } + } + } + }, + "/apis/shorturl.grafana.app/v1alpha1/namespaces/{namespace}/shorturls": { + "get": { + "tags": [ + "ShortURL" + ], + "description": "list objects of kind ShortURL", + "operationId": "listShortURL", + "parameters": [ + { + "name": "allowWatchBookmarks", + "in": "query", + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "name": "continue", + "in": "query", + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "fieldSelector", + "in": "query", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "labelSelector", + "in": "query", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "limit", + "in": "query", + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "name": "resourceVersion", + "in": "query", + "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "resourceVersionMatch", + "in": "query", + "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "sendInitialEvents", + "in": "query", + "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "name": "timeoutSeconds", + "in": "query", + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "name": "watch", + "in": "query", + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "schema": { + "type": "boolean", + "uniqueItems": true + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURLList" + } + }, + "application/json;stream=watch": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURLList" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURLList" + } + }, + "application/vnd.kubernetes.protobuf;stream=watch": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURLList" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURLList" + } + } + } + } + }, + "x-kubernetes-action": "list", + "x-kubernetes-group-version-kind": { + "group": "shorturl.grafana.app", + "version": "v1alpha1", + "kind": "ShortURL" + } + }, + "post": { + "tags": [ + "ShortURL" + ], + "description": "create a ShortURL", + "operationId": "createShortURL", + "parameters": [ + { + "name": "dryRun", + "in": "query", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "fieldManager", + "in": "query", + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "fieldValidation", + "in": "query", + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + } + } + }, + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + } + } + }, + "202": { + "description": "Accepted", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + } + } + } + }, + "x-kubernetes-action": "post", + "x-kubernetes-group-version-kind": { + "group": "shorturl.grafana.app", + "version": "v1alpha1", + "kind": "ShortURL" + } + }, + "delete": { + "tags": [ + "ShortURL" + ], + "description": "delete collection of ShortURL", + "operationId": "deletecollectionShortURL", + "parameters": [ + { + "name": "continue", + "in": "query", + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "dryRun", + "in": "query", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "fieldSelector", + "in": "query", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "gracePeriodSeconds", + "in": "query", + "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "name": "ignoreStoreReadErrorWithClusterBreakingPotential", + "in": "query", + "description": "if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "name": "labelSelector", + "in": "query", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "limit", + "in": "query", + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "name": "orphanDependents", + "in": "query", + "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "name": "propagationPolicy", + "in": "query", + "description": "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.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "resourceVersion", + "in": "query", + "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "resourceVersionMatch", + "in": "query", + "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "sendInitialEvents", + "in": "query", + "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "name": "timeoutSeconds", + "in": "query", + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "schema": { + "type": "integer", + "uniqueItems": true + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + } + } + } + }, + "x-kubernetes-action": "deletecollection", + "x-kubernetes-group-version-kind": { + "group": "shorturl.grafana.app", + "version": "v1alpha1", + "kind": "ShortURL" + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "pretty", + "in": "query", + "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ] + }, + "/apis/shorturl.grafana.app/v1alpha1/namespaces/{namespace}/shorturls/{name}": { + "get": { + "tags": [ + "ShortURL" + ], + "description": "read the specified ShortURL", + "operationId": "getShortURL", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + } + } + } + }, + "x-kubernetes-action": "get", + "x-kubernetes-group-version-kind": { + "group": "shorturl.grafana.app", + "version": "v1alpha1", + "kind": "ShortURL" + } + }, + "put": { + "tags": [ + "ShortURL" + ], + "description": "replace the specified ShortURL", + "operationId": "replaceShortURL", + "parameters": [ + { + "name": "dryRun", + "in": "query", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "fieldManager", + "in": "query", + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "fieldValidation", + "in": "query", + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + } + } + }, + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + } + } + } + }, + "x-kubernetes-action": "put", + "x-kubernetes-group-version-kind": { + "group": "shorturl.grafana.app", + "version": "v1alpha1", + "kind": "ShortURL" + } + }, + "delete": { + "tags": [ + "ShortURL" + ], + "description": "delete a ShortURL", + "operationId": "deleteShortURL", + "parameters": [ + { + "name": "dryRun", + "in": "query", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "gracePeriodSeconds", + "in": "query", + "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "name": "ignoreStoreReadErrorWithClusterBreakingPotential", + "in": "query", + "description": "if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "name": "orphanDependents", + "in": "query", + "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "name": "propagationPolicy", + "in": "query", + "description": "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.", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + } + } + }, + "202": { + "description": "Accepted", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + } + } + } + }, + "x-kubernetes-action": "delete", + "x-kubernetes-group-version-kind": { + "group": "shorturl.grafana.app", + "version": "v1alpha1", + "kind": "ShortURL" + } + }, + "patch": { + "tags": [ + "ShortURL" + ], + "description": "partially update the specified ShortURL", + "operationId": "updateShortURL", + "parameters": [ + { + "name": "dryRun", + "in": "query", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "fieldManager", + "in": "query", + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "fieldValidation", + "in": "query", + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "force", + "in": "query", + "description": "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.", + "schema": { + "type": "boolean", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "application/apply-patch+yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/merge-patch+json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/strategic-merge-patch+json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + } + } + }, + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + } + } + } + }, + "x-kubernetes-action": "patch", + "x-kubernetes-group-version-kind": { + "group": "shorturl.grafana.app", + "version": "v1alpha1", + "kind": "ShortURL" + } + }, + "parameters": [ + { + "name": "name", + "in": "path", + "description": "name of the ShortURL", + "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": "pretty", + "in": "query", + "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ] + }, + "/apis/shorturl.grafana.app/v1alpha1/namespaces/{namespace}/shorturls/{name}/status": { + "get": { + "tags": [ + "ShortURL" + ], + "description": "read status of the specified ShortURL", + "operationId": "getShortURLStatus", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + } + } + } + }, + "x-kubernetes-action": "get", + "x-kubernetes-group-version-kind": { + "group": "shorturl.grafana.app", + "version": "v1alpha1", + "kind": "ShortURL" + } + }, + "put": { + "tags": [ + "ShortURL" + ], + "description": "replace status of the specified ShortURL", + "operationId": "replaceShortURLStatus", + "parameters": [ + { + "name": "dryRun", + "in": "query", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "fieldManager", + "in": "query", + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "fieldValidation", + "in": "query", + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + } + } + }, + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + } + } + } + }, + "x-kubernetes-action": "put", + "x-kubernetes-group-version-kind": { + "group": "shorturl.grafana.app", + "version": "v1alpha1", + "kind": "ShortURL" + } + }, + "patch": { + "tags": [ + "ShortURL" + ], + "description": "partially update status of the specified ShortURL", + "operationId": "updateShortURLStatus", + "parameters": [ + { + "name": "dryRun", + "in": "query", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "fieldManager", + "in": "query", + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "fieldValidation", + "in": "query", + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "force", + "in": "query", + "description": "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.", + "schema": { + "type": "boolean", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "application/apply-patch+yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/merge-patch+json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/strategic-merge-patch+json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + } + } + }, + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + } + } + } + }, + "x-kubernetes-action": "patch", + "x-kubernetes-group-version-kind": { + "group": "shorturl.grafana.app", + "version": "v1alpha1", + "kind": "ShortURL" + } + }, + "parameters": [ + { + "name": "name", + "in": "path", + "description": "name of the ShortURL", + "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": "pretty", + "in": "query", + "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ] + } + }, + "components": { + "schemas": { + "com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL": { + "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": {}, + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + } + ] + }, + "spec": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURLSpec" + } + ] + }, + "status": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURLStatus" + } + ] + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "shorturl.grafana.app", + "kind": "ShortURL", + "version": "v1alpha1" + } + ] + }, + "com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURLList": { + "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", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURL" + } + ] + } + }, + "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": "shorturl.grafana.app", + "kind": "ShortURLList", + "version": "v1alpha1" + } + ] + }, + "com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURLSpec": { + "type": "object", + "required": [ + "path" + ], + "properties": { + "path": { + "description": "The original path to where the short url is linking too e.g. https://localhost:3000/eer8i1kictngga/new-dashboard-with-lib-panel", + "type": "string" + } + } + }, + "com.github.grafana.grafana.apps.shorturl.pkg.apis.shorturl.v1alpha1.ShortURLStatus": { + "type": "object", + "required": [ + "lastSeenAt" + ], + "properties": { + "additionalFields": { + "description": "additionalFields is reserved for future use", + "type": "object", + "additionalProperties": true + }, + "lastSeenAt": { + "description": "The last time the short URL was used, 0 is the initial value", + "type": "integer", + "format": "int64" + }, + "operatorStates": { + "description": "operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.", + "type": "object", + "additionalProperties": { + "type": "object", + "required": [ + "lastEvaluation", + "state" + ], + "properties": { + "descriptiveState": { + "description": "descriptiveState is an optional more descriptive state field which has no requirements on format", + "type": "string" + }, + "details": { + "description": "details contains any extra information that is operator-specific", + "type": "object", + "additionalProperties": true + }, + "lastEvaluation": { + "description": "lastEvaluation is the ResourceVersion last evaluated", + "type": "string" + }, + "state": { + "description": "state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.", + "type": "string", + "enum": [ + "success", + "in_progress", + "failed" + ] + } + } + } + } + } + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.APIResource": { + "description": "APIResource specifies the name of a resource and whether it is namespaced.", + "type": "object", + "required": [ + "name", + "singularName", + "namespaced", + "kind", + "verbs" + ], + "properties": { + "categories": { + "description": "categories is a list of the grouped resources this resource belongs to (e.g. 'all')", + "type": "array", + "items": { + "type": "string", + "default": "" + }, + "x-kubernetes-list-type": "atomic" + }, + "group": { + "description": "group is the preferred group of the resource. Empty implies the group of the containing resource list. For subresources, this may have a different value, for example: Scale\".", + "type": "string" + }, + "kind": { + "description": "kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')", + "type": "string", + "default": "" + }, + "name": { + "description": "name is the plural name of the resource.", + "type": "string", + "default": "" + }, + "namespaced": { + "description": "namespaced indicates if a resource is namespaced or not.", + "type": "boolean", + "default": false + }, + "shortNames": { + "description": "shortNames is a list of suggested short names of the resource.", + "type": "array", + "items": { + "type": "string", + "default": "" + }, + "x-kubernetes-list-type": "atomic" + }, + "singularName": { + "description": "singularName is the singular name of the resource. This allows clients to handle plural and singular opaquely. The singularName is more correct for reporting status on a single item and both singular and plural are allowed from the kubectl CLI interface.", + "type": "string", + "default": "" + }, + "storageVersionHash": { + "description": "The hash value of the storage version, the version this resource is converted to when written to the data store. Value must be treated as opaque by clients. Only equality comparison on the value is valid. This is an alpha feature and may change or be removed in the future. The field is populated by the apiserver only if the StorageVersionHash feature gate is enabled. This field will remain optional even if it graduates.", + "type": "string" + }, + "verbs": { + "description": "verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy)", + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "version": { + "description": "version is the preferred version of the resource. Empty implies the version of the containing resource list For subresources, this may have a different value, for example: v1 (while inside a v1beta1 version of the core resource's group)\".", + "type": "string" + } + } + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList": { + "description": "APIResourceList is a list of APIResource, it is used to expose the name of the resources supported in a specific group and version, and if the resource is namespaced.", + "type": "object", + "required": [ + "groupVersion", + "resources" + ], + "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" + }, + "groupVersion": { + "description": "groupVersion is the group and version this APIResourceList is for.", + "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" + }, + "resources": { + "description": "resources contains the name of the resources and if they are namespaced.", + "type": "array", + "items": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResource" + } + ] + }, + "x-kubernetes-list-type": "atomic" + } + } + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions": { + "description": "DeleteOptions may be provided when deleting an API object.", + "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" + }, + "dryRun": { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "type": "array", + "items": { + "type": "string", + "default": "" + }, + "x-kubernetes-list-type": "atomic" + }, + "gracePeriodSeconds": { + "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", + "type": "integer", + "format": "int64" + }, + "ignoreStoreReadErrorWithClusterBreakingPotential": { + "description": "if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it", + "type": "boolean" + }, + "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" + }, + "orphanDependents": { + "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", + "type": "boolean" + }, + "preconditions": { + "description": "Must be fulfilled before a deletion is carried out. If not possible, a 409 Conflict status will be returned.", + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Preconditions" + } + ] + }, + "propagationPolicy": { + "description": "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.", + "type": "string" + } + } + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1": { + "description": "FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.\n\nEach key is either a '.' representing the field itself, and will always map to an empty set, or a string representing a sub-field or item. The string will follow one of these four formats: 'f:\u003cname\u003e', where \u003cname\u003e is the name of a field in a struct, or key in a map 'v:\u003cvalue\u003e', where \u003cvalue\u003e is the exact json formatted value of a list item 'i:\u003cindex\u003e', where \u003cindex\u003e is position of a item in a list 'k:\u003ckeys\u003e', where \u003ckeys\u003e is a map of a list item's key fields to their unique values If a key maps to an empty Fields value, the field that key represents is part of the set.\n\nThe exact format is defined in sigs.k8s.io/structured-merge-diff", + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta": { + "description": "ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.", + "type": "object", + "properties": { + "continue": { + "description": "continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message.", + "type": "string" + }, + "remainingItemCount": { + "description": "remainingItemCount is the number of subsequent items in the list which are not included in this list response. If the list request contained label or field selectors, then the number of remaining items is unknown and the field will be left unset and omitted during serialization. If the list is complete (either because it is not chunking or because this is the last chunk), then there are no more remaining items and this field will be left unset and omitted during serialization. Servers older than v1.15 do not set this field. The intended use of the remainingItemCount is *estimating* the size of a collection. Clients should not rely on the remainingItemCount to be set or to be exact.", + "type": "integer", + "format": "int64" + }, + "resourceVersion": { + "description": "String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency", + "type": "string" + }, + "selfLink": { + "description": "Deprecated: selfLink is a legacy read-only field that is no longer populated by the system.", + "type": "string" + } + } + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.ManagedFieldsEntry": { + "description": "ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.", + "type": "object", + "properties": { + "apiVersion": { + "description": "APIVersion defines the version of this resource that this field set applies to. The format is \"group/version\" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.", + "type": "string" + }, + "fieldsType": { + "description": "FieldsType is the discriminator for the different fields format and version. There is currently only one possible value: \"FieldsV1\"", + "type": "string" + }, + "fieldsV1": { + "description": "FieldsV1 holds the first JSON version format as described in the \"FieldsV1\" type.", + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1" + } + ] + }, + "manager": { + "description": "Manager is an identifier of the workflow managing these fields.", + "type": "string" + }, + "operation": { + "description": "Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.", + "type": "string" + }, + "subresource": { + "description": "Subresource is the name of the subresource used to update that object, or empty string if the object was updated through the main resource. The value of this field is used to distinguish between managers, even if they share the same name. For example, a status update will be distinct from a regular update using the same manager name. Note that the APIVersion field is not related to the Subresource field and it always corresponds to the version of the main resource.", + "type": "string" + }, + "time": { + "description": "Time is the timestamp of when the ManagedFields entry was added. The timestamp will also be updated if a field is added, the manager changes any of the owned fields value or removes a field. The timestamp does not update when a field is removed from the entry because another manager took it over.", + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + } + ] + } + } + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta": { + "description": "ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.", + "type": "object", + "properties": { + "annotations": { + "description": "Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations", + "type": "object", + "additionalProperties": { + "type": "string", + "default": "" + } + }, + "creationTimestamp": { + "description": "CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC.\n\nPopulated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + } + ] + }, + "deletionGracePeriodSeconds": { + "description": "Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.", + "type": "integer", + "format": "int64" + }, + "deletionTimestamp": { + "description": "DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field is set by the server when a graceful deletion is requested by the user, and is not directly settable by a client. The resource is expected to be deleted (no longer visible from resource lists, and not reachable by name) after the time in this field, once the finalizers list is empty. As long as the finalizers list contains items, deletion is blocked. Once the deletionTimestamp is set, this value may not be unset or be set further into the future, although it may be shortened or the resource may be deleted prior to this time. For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination signal to the containers in the pod. After that 30 seconds, the Kubelet will send a hard termination signal (SIGKILL) to the container and after cleanup, remove the pod from the API. In the presence of network partitions, this object may still exist after this timestamp, until an administrator or automated process can determine the resource is fully terminated. If not set, graceful deletion of the object has not been requested.\n\nPopulated by the system when a graceful deletion is requested. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + } + ] + }, + "finalizers": { + "description": "Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed. Finalizers may be processed and removed in any order. Order is NOT enforced because it introduces significant risk of stuck finalizers. finalizers is a shared field, any actor with permission can reorder it. If the finalizer list is processed in order, then this can lead to a situation in which the component responsible for the first finalizer in the list is waiting for a signal (field value, external system, or other) produced by a component responsible for a finalizer later in the list, resulting in a deadlock. Without enforced ordering finalizers are free to order amongst themselves and are not vulnerable to ordering changes in the list.", + "type": "array", + "items": { + "type": "string", + "default": "" + }, + "x-kubernetes-list-type": "set", + "x-kubernetes-patch-strategy": "merge" + }, + "generateName": { + "description": "GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\n\nIf this field is specified and the generated name exists, the server will return a 409.\n\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency", + "type": "string" + }, + "generation": { + "description": "A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.", + "type": "integer", + "format": "int64" + }, + "labels": { + "description": "Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels", + "type": "object", + "additionalProperties": { + "type": "string", + "default": "" + } + }, + "managedFields": { + "description": "ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like \"ci-cd\". The set of fields is always in the version that the workflow used when modifying the object.", + "type": "array", + "items": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ManagedFieldsEntry" + } + ] + }, + "x-kubernetes-list-type": "atomic" + }, + "name": { + "description": "Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names", + "type": "string" + }, + "namespace": { + "description": "Namespace defines the space within which each name must be unique. An empty namespace is equivalent to the \"default\" namespace, but \"default\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\n\nMust be a DNS_LABEL. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces", + "type": "string" + }, + "ownerReferences": { + "description": "List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.", + "type": "array", + "items": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.OwnerReference" + } + ] + }, + "x-kubernetes-list-map-keys": [ + "uid" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "uid", + "x-kubernetes-patch-strategy": "merge" + }, + "resourceVersion": { + "description": "An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\n\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency", + "type": "string" + }, + "selfLink": { + "description": "Deprecated: selfLink is a legacy read-only field that is no longer populated by the system.", + "type": "string" + }, + "uid": { + "description": "UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\n\nPopulated by the system. Read-only. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids", + "type": "string" + } + } + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.OwnerReference": { + "description": "OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.", + "type": "object", + "required": [ + "apiVersion", + "kind", + "name", + "uid" + ], + "properties": { + "apiVersion": { + "description": "API version of the referent.", + "type": "string", + "default": "" + }, + "blockOwnerDeletion": { + "description": "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. See https://kubernetes.io/docs/concepts/architecture/garbage-collection/#foreground-deletion for how the garbage collector interacts with this field and enforces the foreground deletion. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.", + "type": "boolean" + }, + "controller": { + "description": "If true, this reference points to the managing controller.", + "type": "boolean" + }, + "kind": { + "description": "Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "default": "" + }, + "name": { + "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names", + "type": "string", + "default": "" + }, + "uid": { + "description": "UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids", + "type": "string", + "default": "" + } + }, + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Patch": { + "description": "Patch is provided to give a concrete name and type to the Kubernetes PATCH request body.", + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Preconditions": { + "description": "Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.", + "type": "object", + "properties": { + "resourceVersion": { + "description": "Specifies the target ResourceVersion", + "type": "string" + }, + "uid": { + "description": "Specifies the target UID.", + "type": "string" + } + } + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Status": { + "description": "Status is a return value for calls that don't return other objects.", + "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" + }, + "code": { + "description": "Suggested HTTP return code for this status, 0 if not set.", + "type": "integer", + "format": "int32" + }, + "details": { + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" + } + ], + "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" + }, + "message": { + "description": "A human-readable description of the status of this operation.", + "type": "string" + }, + "metadata": { + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + ] + }, + "reason": { + "description": "A machine-readable description of why this operation is in the \"Failure\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.", + "type": "string" + }, + "status": { + "description": "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", + "type": "string" + } + } + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.StatusCause": { + "description": "StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.", + "type": "object", + "properties": { + "field": { + "description": "The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.\n\nExamples:\n \"name\" - the field \"name\" on the current resource\n \"items[0].name\" - the field \"name\" on the first array entry in \"items\"", + "type": "string" + }, + "message": { + "description": "A human-readable description of the cause of the error. This field may be presented as-is to a reader.", + "type": "string" + }, + "reason": { + "description": "A machine-readable description of the cause of the error. If this value is empty there is no information available.", + "type": "string" + } + } + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails": { + "description": "StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.", + "type": "object", + "properties": { + "causes": { + "description": "The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.", + "type": "array", + "items": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusCause" + } + ] + }, + "x-kubernetes-list-type": "atomic" + }, + "group": { + "description": "The group attribute of the resource associated with the status StatusReason.", + "type": "string" + }, + "kind": { + "description": "The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "name": { + "description": "The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).", + "type": "string" + }, + "retryAfterSeconds": { + "description": "If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action.", + "type": "integer", + "format": "int32" + }, + "uid": { + "description": "UID of the resource. (when there is a single resource which can be described). More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids", + "type": "string" + } + } + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Time": { + "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.", + "type": "string", + "format": "date-time" + } + } + } +} \ No newline at end of file diff --git a/pkg/tests/apis/openapi_test.go b/pkg/tests/apis/openapi_test.go index f5105589c43..9e3e4886724 100644 --- a/pkg/tests/apis/openapi_test.go +++ b/pkg/tests/apis/openapi_test.go @@ -32,6 +32,7 @@ func TestIntegrationOpenAPIs(t *testing.T) { featuremgmt.FlagGrafanaAdvisor, featuremgmt.FlagKubernetesAlertingRules, featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs, // all datasources + featuremgmt.FlagKubernetesShortURLs, }, }) @@ -98,6 +99,9 @@ func TestIntegrationOpenAPIs(t *testing.T) { }, { Group: "rules.alerting.grafana.app", Version: "v0alpha1", + }, { + Group: "shorturl.grafana.app", + Version: "v1alpha1", }} for _, gv := range groups { VerifyOpenAPISnapshots(t, dir, gv, h) diff --git a/public/app/api/clients/shorturl/v1alpha1/baseAPI.ts b/public/app/api/clients/shorturl/v1alpha1/baseAPI.ts new file mode 100644 index 00000000000..8ded2f34c02 --- /dev/null +++ b/public/app/api/clients/shorturl/v1alpha1/baseAPI.ts @@ -0,0 +1,14 @@ +import { createApi } from '@reduxjs/toolkit/query/react'; + +import { createBaseQuery } from 'app/api/createBaseQuery'; +import { getAPIBaseURL } from 'app/api/utils'; + +export const BASE_URL = getAPIBaseURL('shorturl.grafana.app', 'v1alpha1'); + +export const api = createApi({ + reducerPath: 'shortURLAPIv1alpha1', + baseQuery: createBaseQuery({ + baseURL: BASE_URL, + }), + endpoints: () => ({}), +}); diff --git a/public/app/api/clients/shorturl/v1alpha1/endpoints.gen.ts b/public/app/api/clients/shorturl/v1alpha1/endpoints.gen.ts new file mode 100644 index 00000000000..ad9c2da0884 --- /dev/null +++ b/public/app/api/clients/shorturl/v1alpha1/endpoints.gen.ts @@ -0,0 +1,582 @@ +import { api } from './baseAPI'; +export const addTagTypes = ['API Discovery', 'ShortURL'] as const; +const injectedRtkApi = api + .enhanceEndpoints({ + addTagTypes, + }) + .injectEndpoints({ + endpoints: (build) => ({ + getApiResources: build.query({ + query: () => ({ url: `/apis/shorturl.grafana.app/v1alpha1/` }), + providesTags: ['API Discovery'], + }), + listShortUrl: build.query({ + query: (queryArg) => ({ + url: `/shorturls`, + params: { + pretty: queryArg.pretty, + allowWatchBookmarks: queryArg.allowWatchBookmarks, + continue: queryArg['continue'], + fieldSelector: queryArg.fieldSelector, + labelSelector: queryArg.labelSelector, + limit: queryArg.limit, + resourceVersion: queryArg.resourceVersion, + resourceVersionMatch: queryArg.resourceVersionMatch, + sendInitialEvents: queryArg.sendInitialEvents, + timeoutSeconds: queryArg.timeoutSeconds, + watch: queryArg.watch, + }, + }), + providesTags: ['ShortURL'], + }), + createShortUrl: build.mutation({ + query: (queryArg) => ({ + url: `/shorturls`, + method: 'POST', + body: queryArg.shortUrl, + params: { + pretty: queryArg.pretty, + dryRun: queryArg.dryRun, + fieldManager: queryArg.fieldManager, + fieldValidation: queryArg.fieldValidation, + }, + }), + invalidatesTags: ['ShortURL'], + }), + deletecollectionShortUrl: build.mutation({ + query: (queryArg) => ({ + url: `/shorturls`, + method: 'DELETE', + params: { + pretty: queryArg.pretty, + continue: queryArg['continue'], + dryRun: queryArg.dryRun, + fieldSelector: queryArg.fieldSelector, + gracePeriodSeconds: queryArg.gracePeriodSeconds, + ignoreStoreReadErrorWithClusterBreakingPotential: queryArg.ignoreStoreReadErrorWithClusterBreakingPotential, + labelSelector: queryArg.labelSelector, + limit: queryArg.limit, + orphanDependents: queryArg.orphanDependents, + propagationPolicy: queryArg.propagationPolicy, + resourceVersion: queryArg.resourceVersion, + resourceVersionMatch: queryArg.resourceVersionMatch, + sendInitialEvents: queryArg.sendInitialEvents, + timeoutSeconds: queryArg.timeoutSeconds, + }, + }), + invalidatesTags: ['ShortURL'], + }), + getShortUrl: build.query({ + query: (queryArg) => ({ + url: `/shorturls/${queryArg.name}`, + params: { + pretty: queryArg.pretty, + }, + }), + providesTags: ['ShortURL'], + }), + replaceShortUrl: build.mutation({ + query: (queryArg) => ({ + url: `/shorturls/${queryArg.name}`, + method: 'PUT', + body: queryArg.shortUrl, + params: { + pretty: queryArg.pretty, + dryRun: queryArg.dryRun, + fieldManager: queryArg.fieldManager, + fieldValidation: queryArg.fieldValidation, + }, + }), + invalidatesTags: ['ShortURL'], + }), + deleteShortUrl: build.mutation({ + query: (queryArg) => ({ + url: `/shorturls/${queryArg.name}`, + method: 'DELETE', + params: { + pretty: queryArg.pretty, + dryRun: queryArg.dryRun, + gracePeriodSeconds: queryArg.gracePeriodSeconds, + ignoreStoreReadErrorWithClusterBreakingPotential: queryArg.ignoreStoreReadErrorWithClusterBreakingPotential, + orphanDependents: queryArg.orphanDependents, + propagationPolicy: queryArg.propagationPolicy, + }, + }), + invalidatesTags: ['ShortURL'], + }), + updateShortUrl: build.mutation({ + query: (queryArg) => ({ + url: `/shorturls/${queryArg.name}`, + method: 'PATCH', + body: queryArg.patch, + params: { + pretty: queryArg.pretty, + dryRun: queryArg.dryRun, + fieldManager: queryArg.fieldManager, + fieldValidation: queryArg.fieldValidation, + force: queryArg.force, + }, + }), + invalidatesTags: ['ShortURL'], + }), + getShortUrlStatus: build.query({ + query: (queryArg) => ({ + url: `/shorturls/${queryArg.name}/status`, + params: { + pretty: queryArg.pretty, + }, + }), + providesTags: ['ShortURL'], + }), + replaceShortUrlStatus: build.mutation({ + query: (queryArg) => ({ + url: `/shorturls/${queryArg.name}/status`, + method: 'PUT', + body: queryArg.shortUrl, + params: { + pretty: queryArg.pretty, + dryRun: queryArg.dryRun, + fieldManager: queryArg.fieldManager, + fieldValidation: queryArg.fieldValidation, + }, + }), + invalidatesTags: ['ShortURL'], + }), + updateShortUrlStatus: build.mutation({ + query: (queryArg) => ({ + url: `/shorturls/${queryArg.name}/status`, + method: 'PATCH', + body: queryArg.patch, + params: { + pretty: queryArg.pretty, + dryRun: queryArg.dryRun, + fieldManager: queryArg.fieldManager, + fieldValidation: queryArg.fieldValidation, + force: queryArg.force, + }, + }), + invalidatesTags: ['ShortURL'], + }), + }), + overrideExisting: false, + }); +export { injectedRtkApi as generatedAPI }; +export type GetApiResourcesApiResponse = /** status 200 OK */ ApiResourceList; +export type GetApiResourcesApiArg = void; +export type ListShortUrlApiResponse = /** status 200 OK */ ShortUrlList; +export type ListShortUrlApiArg = { + /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */ + pretty?: string; + /** allowWatchBookmarks requests watch events with type "BOOKMARK". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. */ + allowWatchBookmarks?: boolean; + /** The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the "next key". + + This field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications. */ + continue?: string; + /** A selector to restrict the list of returned objects by their fields. Defaults to everything. */ + fieldSelector?: string; + /** A selector to restrict the list of returned objects by their labels. Defaults to everything. */ + labelSelector?: string; + /** limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true. + + The server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned. */ + limit?: number; + /** resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details. + + Defaults to unset */ + resourceVersion?: string; + /** resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details. + + Defaults to unset */ + resourceVersionMatch?: string; + /** `sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic "Bookmark" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `"k8s.io/initial-events-end": "true"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched. + + When `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan + is interpreted as "data at least as new as the provided `resourceVersion`" + and the bookmark event is send when the state is synced + to a `resourceVersion` at least as fresh as the one provided by the ListOptions. + If `resourceVersion` is unset, this is interpreted as "consistent read" and the + bookmark event is send when the state is synced at least to the moment + when request started being processed. + - `resourceVersionMatch` set to any other value or unset + Invalid error is returned. + + Defaults to true if `resourceVersion=""` or `resourceVersion="0"` (for backward compatibility reasons) and to false otherwise. */ + sendInitialEvents?: boolean; + /** Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity. */ + timeoutSeconds?: number; + /** Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion. */ + watch?: boolean; +}; +export type CreateShortUrlApiResponse = /** status 200 OK */ + | ShortUrl + | /** status 201 Created */ ShortUrl + | /** status 202 Accepted */ ShortUrl; +export type CreateShortUrlApiArg = { + /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */ + pretty?: string; + /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */ + dryRun?: string; + /** fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. */ + fieldManager?: string; + /** fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered. */ + fieldValidation?: string; + shortUrl: ShortUrl; +}; +export type DeletecollectionShortUrlApiResponse = /** status 200 OK */ Status; +export type DeletecollectionShortUrlApiArg = { + /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */ + pretty?: string; + /** The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the "next key". + + This field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications. */ + continue?: string; + /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */ + dryRun?: string; + /** A selector to restrict the list of returned objects by their fields. Defaults to everything. */ + fieldSelector?: string; + /** The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately. */ + gracePeriodSeconds?: number; + /** if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it */ + ignoreStoreReadErrorWithClusterBreakingPotential?: boolean; + /** A selector to restrict the list of returned objects by their labels. Defaults to everything. */ + labelSelector?: string; + /** limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true. + + The server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned. */ + limit?: number; + /** Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the "orphan" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both. */ + orphanDependents?: boolean; + /** 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; + /** resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details. + + Defaults to unset */ + resourceVersion?: string; + /** resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details. + + Defaults to unset */ + resourceVersionMatch?: string; + /** `sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic "Bookmark" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `"k8s.io/initial-events-end": "true"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched. + + When `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan + is interpreted as "data at least as new as the provided `resourceVersion`" + and the bookmark event is send when the state is synced + to a `resourceVersion` at least as fresh as the one provided by the ListOptions. + If `resourceVersion` is unset, this is interpreted as "consistent read" and the + bookmark event is send when the state is synced at least to the moment + when request started being processed. + - `resourceVersionMatch` set to any other value or unset + Invalid error is returned. + + Defaults to true if `resourceVersion=""` or `resourceVersion="0"` (for backward compatibility reasons) and to false otherwise. */ + sendInitialEvents?: boolean; + /** Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity. */ + timeoutSeconds?: number; +}; +export type GetShortUrlApiResponse = /** status 200 OK */ ShortUrl; +export type GetShortUrlApiArg = { + /** name of the ShortURL */ + name: string; + /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */ + pretty?: string; +}; +export type ReplaceShortUrlApiResponse = /** status 200 OK */ ShortUrl | /** status 201 Created */ ShortUrl; +export type ReplaceShortUrlApiArg = { + /** name of the ShortURL */ + name: string; + /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */ + pretty?: string; + /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */ + dryRun?: string; + /** fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. */ + fieldManager?: string; + /** fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered. */ + fieldValidation?: string; + shortUrl: ShortUrl; +}; +export type DeleteShortUrlApiResponse = /** status 200 OK */ Status | /** status 202 Accepted */ Status; +export type DeleteShortUrlApiArg = { + /** name of the ShortURL */ + name: string; + /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */ + pretty?: string; + /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */ + dryRun?: string; + /** The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately. */ + gracePeriodSeconds?: number; + /** if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it */ + ignoreStoreReadErrorWithClusterBreakingPotential?: boolean; + /** Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the "orphan" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both. */ + orphanDependents?: boolean; + /** 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 UpdateShortUrlApiResponse = /** status 200 OK */ ShortUrl | /** status 201 Created */ ShortUrl; +export type UpdateShortUrlApiArg = { + /** name of the ShortURL */ + name: string; + /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */ + pretty?: string; + /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */ + dryRun?: string; + /** fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch). */ + fieldManager?: string; + /** fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered. */ + fieldValidation?: string; + /** Force is going to "force" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests. */ + force?: boolean; + patch: Patch; +}; +export type GetShortUrlStatusApiResponse = /** status 200 OK */ ShortUrl; +export type GetShortUrlStatusApiArg = { + /** name of the ShortURL */ + name: string; + /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */ + pretty?: string; +}; +export type ReplaceShortUrlStatusApiResponse = /** status 200 OK */ ShortUrl | /** status 201 Created */ ShortUrl; +export type ReplaceShortUrlStatusApiArg = { + /** name of the ShortURL */ + name: string; + /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */ + pretty?: string; + /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */ + dryRun?: string; + /** fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. */ + fieldManager?: string; + /** fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered. */ + fieldValidation?: string; + shortUrl: ShortUrl; +}; +export type UpdateShortUrlStatusApiResponse = /** status 200 OK */ ShortUrl | /** status 201 Created */ ShortUrl; +export type UpdateShortUrlStatusApiArg = { + /** name of the ShortURL */ + name: string; + /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */ + pretty?: string; + /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */ + dryRun?: string; + /** fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch). */ + fieldManager?: string; + /** fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered. */ + fieldValidation?: string; + /** Force is going to "force" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests. */ + force?: boolean; + patch: Patch; +}; +export type ApiResource = { + /** categories is a list of the grouped resources this resource belongs to (e.g. 'all') */ + categories?: string[]; + /** group is the preferred group of the resource. Empty implies the group of the containing resource list. For subresources, this may have a different value, for example: Scale". */ + group?: string; + /** kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo') */ + kind: string; + /** name is the plural name of the resource. */ + name: string; + /** namespaced indicates if a resource is namespaced or not. */ + namespaced: boolean; + /** shortNames is a list of suggested short names of the resource. */ + shortNames?: string[]; + /** singularName is the singular name of the resource. This allows clients to handle plural and singular opaquely. The singularName is more correct for reporting status on a single item and both singular and plural are allowed from the kubectl CLI interface. */ + singularName: string; + /** The hash value of the storage version, the version this resource is converted to when written to the data store. Value must be treated as opaque by clients. Only equality comparison on the value is valid. This is an alpha feature and may change or be removed in the future. The field is populated by the apiserver only if the StorageVersionHash feature gate is enabled. This field will remain optional even if it graduates. */ + storageVersionHash?: string; + /** verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy) */ + verbs: string[]; + /** version is the preferred version of the resource. Empty implies the version of the containing resource list For subresources, this may have a different value, for example: v1 (while inside a v1beta1 version of the core resource's group)". */ + version?: string; +}; +export type ApiResourceList = { + /** 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; + /** groupVersion is the group and version this APIResourceList is for. */ + groupVersion: 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; + /** resources contains the name of the resources and if they are namespaced. */ + resources: ApiResource[]; +}; +export type Time = string; +export type FieldsV1 = object; +export type ManagedFieldsEntry = { + /** APIVersion defines the version of this resource that this field set applies to. The format is "group/version" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted. */ + apiVersion?: string; + /** FieldsType is the discriminator for the different fields format and version. There is currently only one possible value: "FieldsV1" */ + fieldsType?: string; + /** FieldsV1 holds the first JSON version format as described in the "FieldsV1" type. */ + fieldsV1?: FieldsV1; + /** Manager is an identifier of the workflow managing these fields. */ + manager?: string; + /** Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'. */ + operation?: string; + /** Subresource is the name of the subresource used to update that object, or empty string if the object was updated through the main resource. The value of this field is used to distinguish between managers, even if they share the same name. For example, a status update will be distinct from a regular update using the same manager name. Note that the APIVersion field is not related to the Subresource field and it always corresponds to the version of the main resource. */ + subresource?: string; + /** Time is the timestamp of when the ManagedFields entry was added. The timestamp will also be updated if a field is added, the manager changes any of the owned fields value or removes a field. The timestamp does not update when a field is removed from the entry because another manager took it over. */ + time?: Time; +}; +export type OwnerReference = { + /** API version of the referent. */ + apiVersion: string; + /** If true, AND if the owner has the "foregroundDeletion" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. See https://kubernetes.io/docs/concepts/architecture/garbage-collection/#foreground-deletion for how the garbage collector interacts with this field and enforces the foreground deletion. Defaults to false. To set this field, a user needs "delete" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned. */ + blockOwnerDeletion?: boolean; + /** If true, this reference points to the managing controller. */ + controller?: boolean; + /** Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds */ + kind: string; + /** Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names */ + name: string; + /** UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids */ + uid: string; +}; +export type ObjectMeta = { + /** Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations */ + annotations?: { + [key: string]: string; + }; + /** CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC. + + Populated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata */ + creationTimestamp?: Time; + /** Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only. */ + deletionGracePeriodSeconds?: number; + /** DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field is set by the server when a graceful deletion is requested by the user, and is not directly settable by a client. The resource is expected to be deleted (no longer visible from resource lists, and not reachable by name) after the time in this field, once the finalizers list is empty. As long as the finalizers list contains items, deletion is blocked. Once the deletionTimestamp is set, this value may not be unset or be set further into the future, although it may be shortened or the resource may be deleted prior to this time. For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination signal to the containers in the pod. After that 30 seconds, the Kubelet will send a hard termination signal (SIGKILL) to the container and after cleanup, remove the pod from the API. In the presence of network partitions, this object may still exist after this timestamp, until an administrator or automated process can determine the resource is fully terminated. If not set, graceful deletion of the object has not been requested. + + Populated by the system when a graceful deletion is requested. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata */ + deletionTimestamp?: Time; + /** Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed. Finalizers may be processed and removed in any order. Order is NOT enforced because it introduces significant risk of stuck finalizers. finalizers is a shared field, any actor with permission can reorder it. If the finalizer list is processed in order, then this can lead to a situation in which the component responsible for the first finalizer in the list is waiting for a signal (field value, external system, or other) produced by a component responsible for a finalizer later in the list, resulting in a deadlock. Without enforced ordering finalizers are free to order amongst themselves and are not vulnerable to ordering changes in the list. */ + finalizers?: string[]; + /** GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server. + + If this field is specified and the generated name exists, the server will return a 409. + + Applied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency */ + generateName?: string; + /** A sequence number representing a specific generation of the desired state. Populated by the system. Read-only. */ + generation?: number; + /** Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels */ + labels?: { + [key: string]: string; + }; + /** ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like "ci-cd". The set of fields is always in the version that the workflow used when modifying the object. */ + managedFields?: ManagedFieldsEntry[]; + /** Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names */ + name?: string; + /** Namespace defines the space within which each name must be unique. An empty namespace is equivalent to the "default" namespace, but "default" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty. + + Must be a DNS_LABEL. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces */ + namespace?: string; + /** List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller. */ + ownerReferences?: OwnerReference[]; + /** An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources. + + Populated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency */ + resourceVersion?: string; + /** Deprecated: selfLink is a legacy read-only field that is no longer populated by the system. */ + selfLink?: string; + /** UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations. + + Populated by the system. Read-only. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids */ + uid?: string; +}; +export type ShortUrlSpec = { + /** The original path to where the short url is linking too e.g. https://localhost:3000/eer8i1kictngga/new-dashboard-with-lib-panel */ + path: string; +}; +export type ShortUrlStatus = { + /** additionalFields is reserved for future use */ + additionalFields?: { + [key: string]: any; + }; + /** The last time the short URL was used, 0 is the initial value */ + lastSeenAt: number; + /** operatorStates is a map of operator ID to operator state evaluations. + Any operator which consumes this kind SHOULD add its state evaluation information to this field. */ + operatorStates?: { + [key: string]: { + /** descriptiveState is an optional more descriptive state field which has no requirements on format */ + descriptiveState?: string; + /** details contains any extra information that is operator-specific */ + details?: { + [key: string]: any; + }; + /** lastEvaluation is the ResourceVersion last evaluated */ + lastEvaluation: string; + /** state describes the state of the lastEvaluation. + It is limited to three possible states for machine evaluation. */ + state: 'success' | 'in_progress' | 'failed'; + }; + }; +}; +export type ShortUrl = { + /** 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?: ObjectMeta; + spec?: ShortUrlSpec; + status?: ShortUrlStatus; +}; +export type ListMeta = { + /** continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message. */ + continue?: string; + /** remainingItemCount is the number of subsequent items in the list which are not included in this list response. If the list request contained label or field selectors, then the number of remaining items is unknown and the field will be left unset and omitted during serialization. If the list is complete (either because it is not chunking or because this is the last chunk), then there are no more remaining items and this field will be left unset and omitted during serialization. Servers older than v1.15 do not set this field. The intended use of the remainingItemCount is *estimating* the size of a collection. Clients should not rely on the remainingItemCount to be set or to be exact. */ + remainingItemCount?: number; + /** String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency */ + resourceVersion?: string; + /** Deprecated: selfLink is a legacy read-only field that is no longer populated by the system. */ + selfLink?: string; +}; +export type ShortUrlList = { + /** 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: ShortUrl[]; + /** 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 StatusCause = { + /** The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional. + + Examples: + "name" - the field "name" on the current resource + "items[0].name" - the field "name" on the first array entry in "items" */ + field?: string; + /** A human-readable description of the cause of the error. This field may be presented as-is to a reader. */ + message?: string; + /** A machine-readable description of the cause of the error. If this value is empty there is no information available. */ + reason?: string; +}; +export type StatusDetails = { + /** The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes. */ + causes?: StatusCause[]; + /** The group attribute of the resource associated with the status StatusReason. */ + group?: string; + /** The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds */ + kind?: string; + /** The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described). */ + name?: string; + /** If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action. */ + retryAfterSeconds?: number; + /** UID of the resource. (when there is a single resource which can be described). More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids */ + uid?: string; +}; +export type Status = { + /** 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; + /** Suggested HTTP return code for this status, 0 if not set. */ + code?: number; + /** Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type. */ + details?: StatusDetails; + /** 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; + /** A human-readable description of the status of this operation. */ + message?: string; + /** Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds */ + metadata?: ListMeta; + /** A machine-readable description of why this operation is in the "Failure" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it. */ + reason?: string; + /** 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 Patch = object; diff --git a/public/app/api/clients/shorturl/v1alpha1/index.ts b/public/app/api/clients/shorturl/v1alpha1/index.ts new file mode 100644 index 00000000000..415f7a0c474 --- /dev/null +++ b/public/app/api/clients/shorturl/v1alpha1/index.ts @@ -0,0 +1,3 @@ +import { generatedAPI } from './endpoints.gen'; + +export const shortURLAPIv1alpha1 = generatedAPI.enhanceEndpoints({}); diff --git a/public/app/core/reducers/root.ts b/public/app/core/reducers/root.ts index f740febeeae..056c4bb0015 100644 --- a/public/app/core/reducers/root.ts +++ b/public/app/core/reducers/root.ts @@ -4,6 +4,7 @@ import { AnyAction, combineReducers } from 'redux'; import { alertingAPI as alertingPackageAPI } from '@grafana/alerting/unstable'; import { dashboardAPIv0alpha1 } from 'app/api/clients/dashboard/v0alpha1'; import { rulesAPIv0alpha1 } from 'app/api/clients/rules/v0alpha1'; +import { shortURLAPIv1alpha1 } from 'app/api/clients/shorturl/v1alpha1'; import sharedReducers from 'app/core/reducers'; import ldapReducers from 'app/features/admin/state/reducers'; import alertingReducers from 'app/features/alerting/state/reducers'; @@ -75,6 +76,7 @@ const rootReducers = { [advisorAPIv0alpha1.reducerPath]: advisorAPIv0alpha1.reducer, [dashboardAPIv0alpha1.reducerPath]: dashboardAPIv0alpha1.reducer, [rulesAPIv0alpha1.reducerPath]: rulesAPIv0alpha1.reducer, + [shortURLAPIv1alpha1.reducerPath]: shortURLAPIv1alpha1.reducer, // PLOP_INJECT_REDUCER // Used by the API client generator }; diff --git a/public/app/core/utils/shortLinks.test.ts b/public/app/core/utils/shortLinks.test.ts index 406aa73e99f..d07c4940680 100644 --- a/public/app/core/utils/shortLinks.test.ts +++ b/public/app/core/utils/shortLinks.test.ts @@ -2,14 +2,18 @@ import { LogRowModel } from '@grafana/data'; import { config } from '@grafana/runtime'; import { createLogRow } from 'app/features/logs/components/mocks/logRow'; -import { createShortLink, createAndCopyShortLink, getLogsPermalinkRange } from './shortLinks'; +import { ShortURL } from '../../../../apps/shorturl/plugin/src/generated/shorturl/v1alpha1/shorturl_object_gen'; +import { defaultSpec } from '../../../../apps/shorturl/plugin/src/generated/shorturl/v1alpha1/types.spec.gen'; +import { defaultStatus } from '../../../../apps/shorturl/plugin/src/generated/shorturl/v1alpha1/types.status.gen'; + +import { createShortLink, createAndCopyShortLink, getLogsPermalinkRange, buildShortUrl } from './shortLinks'; jest.mock('@grafana/runtime', () => ({ ...jest.requireActual('@grafana/runtime'), getBackendSrv: () => { return { post: () => { - return Promise.resolve({ url: 'www.short.com' }); + return Promise.resolve({ url: 'www.test.grafana.com/goto/bewyw48durgu8d?orgId=1' }); }, }; }, @@ -24,12 +28,21 @@ beforeEach(() => { }); document.execCommand = jest.fn(); + config.featureToggles.useKubernetesShortURLsAPI = false; }); describe('createShortLink', () => { it('creates short link', async () => { - const shortUrl = await createShortLink('www.verylonglinkwehavehere.com'); - expect(shortUrl).toBe('www.short.com'); + const shortUrl = await createShortLink('d/edhmipji89b0gb/welcome?orgId=1&from=now-6h&to=now&timezone=browser'); + expect(shortUrl).toBe('www.test.grafana.com/goto/bewyw48durgu8d?orgId=1'); + }); +}); + +describe('createShortLink using k8s API', () => { + it('creates short link', async () => { + config.featureToggles.useKubernetesShortURLsAPI = true; + const shortUrl = await createShortLink('d/edhmipji89b0gb/welcome?orgId=1&from=now-6h&to=now&timezone=browser'); + expect(shortUrl).toBe('www.test.grafana.com/goto/bewyw48durgu8d?orgId=1'); }); }); @@ -41,14 +54,14 @@ describe('createAndCopyShortLink', () => { }, }); document.execCommand = jest.fn(); - await createAndCopyShortLink('www.verylonglinkwehavehere.com'); + await createAndCopyShortLink('www.test.grafana.com'); expect(document.execCommand).toHaveBeenCalledWith('copy'); }); it('copies short link to clipboard via navigator.clipboard.writeText when ClipboardItem is undefined', async () => { window.isSecureContext = true; - await createAndCopyShortLink('www.verylonglinkwehavehere.com'); - expect(navigator.clipboard.writeText).toHaveBeenCalledWith('www.short.com'); + await createAndCopyShortLink('d/edhmipji89b0gb/welcome?orgId=1&from=now-6h&to=now&timezone=browser'); + expect(navigator.clipboard.writeText).toHaveBeenCalledWith('www.test.grafana.com/goto/bewyw48durgu8d?orgId=1'); }); it('copies short link to clipboard via navigator.clipboard.write and ClipboardItem when it is defined', async () => { @@ -59,11 +72,61 @@ describe('createAndCopyShortLink', () => { supports: jest.fn().mockReturnValue(true), // eslint-disable-next-line })) as any; - await createAndCopyShortLink('www.verylonglinkwehavehere.com'); + await createAndCopyShortLink('d/edhmipji89b0gb/welcome?orgId=1&from=now-6h&to=now&timezone=browser'); expect(navigator.clipboard.write).toHaveBeenCalled(); }); }); +describe('buildShortUrl', () => { + // Mock window.location + const mockLocation = { + protocol: 'https:', + host: 'grafana.example.com', + }; + + beforeEach(() => { + Object.defineProperty(window, 'location', { + value: mockLocation, + writable: true, + }); + config.appSubUrl = ''; + }); + + it('builds short URL with metadata name and namespace', () => { + const shortUrl: ShortURL = { + kind: 'ShortURL', + apiVersion: 'shorturl.grafana.app/v1alpha1', + metadata: { + name: 'abc123def', + namespace: 'org-5', + }, + spec: defaultSpec(), + status: defaultStatus(), + }; + + const result = buildShortUrl(shortUrl); + expect(result).toBe('https://grafana.example.com/goto/abc123def?orgId=org-5'); + }); + + it('builds short URL with appSubUrl configured', () => { + config.appSubUrl = '/grafana'; + + const shortUrl: ShortURL = { + kind: 'ShortURL', + apiVersion: 'shorturl.grafana.app/v1alpha1', + metadata: { + name: 'xyz789', + namespace: 'org-1', + }, + spec: defaultSpec(), + status: defaultStatus(), + }; + + const result = buildShortUrl(shortUrl); + expect(result).toBe('https://grafana.example.com/grafana/goto/xyz789?orgId=org-1'); + }); +}); + describe('getLogsPermalinkRange', () => { let row: LogRowModel, rows: LogRowModel[]; beforeEach(() => { diff --git a/public/app/core/utils/shortLinks.ts b/public/app/core/utils/shortLinks.ts index 53ebf12af8b..573cfff2627 100644 --- a/public/app/core/utils/shortLinks.ts +++ b/public/app/core/utils/shortLinks.ts @@ -10,6 +10,8 @@ import { DashboardScene } from 'app/features/dashboard-scene/scene/DashboardScen import { getDashboardUrl } from 'app/features/dashboard-scene/utils/getDashboardUrl'; import { dispatch } from 'app/store/store'; +import { ShortURL } from '../../../../apps/shorturl/plugin/src/generated/shorturl/v1alpha1/shorturl_object_gen'; +import { BASE_URL as k8sShortURLBaseAPI } from '../../api/clients/shorturl/v1alpha1/baseAPI'; import { ShareLinkConfiguration } from '../../features/dashboard-scene/sharing/ShareButton/utils'; import { copyStringToClipboard } from './explore'; @@ -18,6 +20,13 @@ function buildHostUrl() { return `${window.location.protocol}//${window.location.host}${config.appSubUrl}`; } +export function buildShortUrl(k8sShortUrl: ShortURL) { + const key = k8sShortUrl.metadata.name; + const orgId = k8sShortUrl.metadata.namespace; + const hostUrl = buildHostUrl(); + return `${hostUrl}/goto/${key}?orgId=${orgId}`; +} + function getRelativeURLPath(url: string) { let path = url.replace(buildHostUrl(), ''); return path.startsWith('/') ? path.substring(1, path.length) : path; @@ -25,10 +34,23 @@ function getRelativeURLPath(url: string) { export const createShortLink = memoizeOne(async function (path: string) { try { - const shortLink = await getBackendSrv().post(`/api/short-urls`, { - path: getRelativeURLPath(path), - }); - return shortLink.url; + if (config.featureToggles.useKubernetesShortURLsAPI) { + // TODO: this is not ideal, we should use the RTK API but we can't call a hook from here and + // this util function is being called from several places, will require a bigger refactor including some code that + // is deprecated. + const k8sShortUrl: ShortURL = await getBackendSrv().post(`${k8sShortURLBaseAPI}/shorturls`, { + spec: { + path: getRelativeURLPath(path), + }, + }); + return buildShortUrl(k8sShortUrl); + } else { + // Old short URL API + const shortLink = await getBackendSrv().post(`/api/short-urls`, { + path: getRelativeURLPath(path), + }); + return shortLink.url; + } } catch (err) { console.error('Error when creating shortened link: ', err); dispatch(notifyApp(createErrorNotification('Error generating shortened link'))); diff --git a/public/app/store/configureStore.ts b/public/app/store/configureStore.ts index 0c812f3c90d..0eeb3c0f352 100644 --- a/public/app/store/configureStore.ts +++ b/public/app/store/configureStore.ts @@ -5,6 +5,7 @@ import { Middleware } from 'redux'; import { alertingAPI as alertingPackageAPI } from '@grafana/alerting/unstable'; import { dashboardAPIv0alpha1 } from 'app/api/clients/dashboard/v0alpha1'; import { rulesAPIv0alpha1 } from 'app/api/clients/rules/v0alpha1'; +import { shortURLAPIv1alpha1 } from 'app/api/clients/shorturl/v1alpha1'; import { browseDashboardsAPI } from 'app/features/browse-dashboards/api/browseDashboardsAPI'; import { publicDashboardApi } from 'app/features/dashboard/api/publicDashboardApi'; import { cloudMigrationAPI } from 'app/features/migrate-to-cloud/api'; @@ -57,6 +58,7 @@ export function configureStore(initialState?: Partial) { advisorAPIv0alpha1.middleware, dashboardAPIv0alpha1.middleware, rulesAPIv0alpha1.middleware, + shortURLAPIv1alpha1.middleware, // PLOP_INJECT_MIDDLEWARE // Used by the API client generator ...extraMiddleware diff --git a/scripts/generate-rtk-apis.ts b/scripts/generate-rtk-apis.ts index 31e850ee3cd..d5b2529ea19 100644 --- a/scripts/generate-rtk-apis.ts +++ b/scripts/generate-rtk-apis.ts @@ -90,6 +90,11 @@ const config: ConfigFile = { tag: true, }, + '../public/app/api/clients/shorturl/v1alpha1/endpoints.gen.ts': { + apiFile: '../public/app/api/clients/shorturl/v1alpha1/baseAPI.ts', + schemaFile: '../data/openapi/shorturl.grafana.app-v1alpha1.json', + tag: true, + }, '../public/app/api/clients/rules/v0alpha1/endpoints.gen.ts': { apiFile: '../public/app/api/clients/rules/v0alpha1/baseAPI.ts', schemaFile: '../data/openapi/rules.alerting.grafana.app-v0alpha1.json', From a3c95e1375ea1e7cdcc76d8d9bc1e784da036cdf Mon Sep 17 00:00:00 2001 From: "grafana-pr-automation[bot]" <140550294+grafana-pr-automation[bot]@users.noreply.github.com> Date: Sun, 14 Sep 2025 00:30:21 +0000 Subject: [PATCH 16/33] I18n: Download translations from Crowdin (#111052) 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 | 54 ++++++++++++++++++++++++----- public/locales/de-DE/grafana.json | 50 +++++++++++++++++++++----- public/locales/es-ES/grafana.json | 50 +++++++++++++++++++++----- public/locales/fr-FR/grafana.json | 50 +++++++++++++++++++++----- public/locales/hu-HU/grafana.json | 50 +++++++++++++++++++++----- public/locales/id-ID/grafana.json | 48 ++++++++++++++++++++----- public/locales/it-IT/grafana.json | 50 +++++++++++++++++++++----- public/locales/ja-JP/grafana.json | 48 ++++++++++++++++++++----- public/locales/ko-KR/grafana.json | 48 ++++++++++++++++++++----- public/locales/nl-NL/grafana.json | 50 +++++++++++++++++++++----- public/locales/pl-PL/grafana.json | 54 ++++++++++++++++++++++++----- public/locales/pt-BR/grafana.json | 50 +++++++++++++++++++++----- public/locales/pt-PT/grafana.json | 50 +++++++++++++++++++++----- public/locales/ru-RU/grafana.json | 54 ++++++++++++++++++++++++----- public/locales/sv-SE/grafana.json | 50 +++++++++++++++++++++----- public/locales/tr-TR/grafana.json | 50 +++++++++++++++++++++----- public/locales/zh-Hans/grafana.json | 48 ++++++++++++++++++++----- public/locales/zh-Hant/grafana.json | 48 ++++++++++++++++++++----- 18 files changed, 758 insertions(+), 144 deletions(-) diff --git a/public/locales/cs-CZ/grafana.json b/public/locales/cs-CZ/grafana.json index 78326412626..6aaa48868de 100644 --- a/public/locales/cs-CZ/grafana.json +++ b/public/locales/cs-CZ/grafana.json @@ -9697,10 +9697,9 @@ } }, "logs-controls": { + "collapse": "", "deduplication": "Deduplikace", - "disable-highlighting": "Zakázat zvýraznění", "disable-prettify-json": "Sbalit protokoly JSON", - "display-level": "Zobrazit úrovně", "display-level-all": "Všechny úrovně", "download": "Stáhnout protokoly", "download-logs": { @@ -9708,18 +9707,39 @@ "json": "json", "txt": "txt" }, - "enable-highlighting": "Povolit zvýraznění", "escape-newlines": "Opravit nesprávně uniklé sekvence nového řádku a záložek v řádcích protokolu", - "font-size-default": "Použít malou velikost písma", - "font-size-small": "Použít výchozí velikost písma", + "expand": "", + "filter-levels": "", + "font-large": "", + "font-small": "", "hide-search": "Zavřít vyhledávání", "hide-timestamps": "Skrýt časová razítka", "hide-unique-labels": "Skrýt jedinečné štítky", + "label": { + "collapse": "", + "disable-highlighting": "", + "enable-highlighting": "", + "escape-newlines": "", + "expand": "" + }, + "labels": { + "font-large": "", + "font-small": "", + "hide-search": "", + "newest-first": "", + "oldest-first": "", + "show-search": "" + }, "line-wrapping": { "enable": "", "enable-prettify": "", "hide": "", - "label": "" + "state": { + "hide": "", + "json": "", + "wrap": "" + }, + "tooltip": "" }, "newest-first": "Seřazeno od nejnovějších protokolů – kliknutím zobrazíte nejstarší protokoly jako první", "oldest-first": "Seřazeno od nejstarších protokolů – kliknutím zobrazíte nejnovější protokoly jako první", @@ -9735,8 +9755,18 @@ "timestamp": { "hide": "", "label": "", + "label-hide": "", + "label-ms": "", + "label-ns": "", "milliseconds": "", - "nanoseconds": "" + "nanoseconds": "", + "tooltip": "" + }, + "tooltip": { + "disable-highlighting": "", + "download": "", + "enable-highlighting": "", + "filter-level": "" }, "unwrap-lines": "Rozbalit řádky", "wrap-lines": "Zalomit řádky" @@ -11433,7 +11463,15 @@ }, "folder-repository-list": { "no-results-matching-your-query": "Vašemu dotazu neodpovídají žádné výsledky", - "placeholder-search": "Hledat" + "placeholder-search": "Hledat", + "all-resources-managed_one": "", + "all-resources-managed_few": "", + "all-resources-managed_many": "", + "all-resources-managed_other": "", + "partial-managed_one": "", + "partial-managed_few": "", + "partial-managed_many": "", + "partial-managed_other": "" }, "get-default-values": { "title": { diff --git a/public/locales/de-DE/grafana.json b/public/locales/de-DE/grafana.json index 75c5062dcf8..ee2a8d283f3 100644 --- a/public/locales/de-DE/grafana.json +++ b/public/locales/de-DE/grafana.json @@ -9633,10 +9633,9 @@ } }, "logs-controls": { + "collapse": "", "deduplication": "Deduplizierung", - "disable-highlighting": "Markierung deaktivieren", "disable-prettify-json": "JSON-Logs einklappen", - "display-level": "Ebenen anzeigen", "display-level-all": "Alle Ebenen", "download": "Logs herunterladen", "download-logs": { @@ -9644,18 +9643,39 @@ "json": "json", "txt": "txt" }, - "enable-highlighting": "Markierung aktivieren", "escape-newlines": "Falsch dargestellte Zeilenumbrüche und Tab-Sequenzen in Log-Zeilen korrigieren", - "font-size-default": "Kleine Schriftgröße verwenden", - "font-size-small": "Standardschriftgröße verwenden", + "expand": "", + "filter-levels": "", + "font-large": "", + "font-small": "", "hide-search": "Suche schließen", "hide-timestamps": "Zeitstempel ausblenden", "hide-unique-labels": "Eindeutige Labels ausblenden", + "label": { + "collapse": "", + "disable-highlighting": "", + "enable-highlighting": "", + "escape-newlines": "", + "expand": "" + }, + "labels": { + "font-large": "", + "font-small": "", + "hide-search": "", + "newest-first": "", + "oldest-first": "", + "show-search": "" + }, "line-wrapping": { "enable": "", "enable-prettify": "", "hide": "", - "label": "" + "state": { + "hide": "", + "json": "", + "wrap": "" + }, + "tooltip": "" }, "newest-first": "Sortiert nach neuesten Logs zuerst – klicken Sie, um die ältesten zuerst anzuzeigen", "oldest-first": "Sortiert nach ältesten Logs zuerst – klicken Sie, um die neuesten zuerst anzuzeigen", @@ -9671,8 +9691,18 @@ "timestamp": { "hide": "", "label": "", + "label-hide": "", + "label-ms": "", + "label-ns": "", "milliseconds": "", - "nanoseconds": "" + "nanoseconds": "", + "tooltip": "" + }, + "tooltip": { + "disable-highlighting": "", + "download": "", + "enable-highlighting": "", + "filter-level": "" }, "unwrap-lines": "Zeilenumbruch aufheben", "wrap-lines": "Zeilen umbrechen" @@ -11355,7 +11385,11 @@ }, "folder-repository-list": { "no-results-matching-your-query": "Keine passenden Ergebnisse zu Ihrer Abfrage", - "placeholder-search": "Suche" + "placeholder-search": "Suche", + "all-resources-managed_one": "", + "all-resources-managed_other": "", + "partial-managed_one": "", + "partial-managed_other": "" }, "get-default-values": { "title": { diff --git a/public/locales/es-ES/grafana.json b/public/locales/es-ES/grafana.json index 7f10dcdd53c..1d5d25b0449 100644 --- a/public/locales/es-ES/grafana.json +++ b/public/locales/es-ES/grafana.json @@ -9633,10 +9633,9 @@ } }, "logs-controls": { + "collapse": "", "deduplication": "Deduplicación", - "disable-highlighting": "Desactivar resaltado", "disable-prettify-json": "Contraer logs JSON", - "display-level": "Mostrar niveles", "display-level-all": "Todos los niveles", "download": "Descargar registros", "download-logs": { @@ -9644,18 +9643,39 @@ "json": "json", "txt": "txt" }, - "enable-highlighting": "Activar resaltado", "escape-newlines": "Corregir las secuencias de tabulación y de nueva línea que escaparon incorrectamente en las líneas de log", - "font-size-default": "Utilizar tamaño de fuente pequeño", - "font-size-small": "Utilizar tamaño de fuente predeterminado", + "expand": "", + "filter-levels": "", + "font-large": "", + "font-small": "", "hide-search": "Cerrar la búsqueda", "hide-timestamps": "Ocultar marcas de tiempo", "hide-unique-labels": "Ocultar etiquetas únicas", + "label": { + "collapse": "", + "disable-highlighting": "", + "enable-highlighting": "", + "escape-newlines": "", + "expand": "" + }, + "labels": { + "font-large": "", + "font-small": "", + "hide-search": "", + "newest-first": "", + "oldest-first": "", + "show-search": "" + }, "line-wrapping": { "enable": "", "enable-prettify": "", "hide": "", - "label": "" + "state": { + "hide": "", + "json": "", + "wrap": "" + }, + "tooltip": "" }, "newest-first": "Ordenado por los logs más nuevos primero: haga clic para mostrar los más antiguos primero", "oldest-first": "Ordenado por los logs más antiguos primero: haga clic para mostrar los más nuevos primero", @@ -9671,8 +9691,18 @@ "timestamp": { "hide": "", "label": "", + "label-hide": "", + "label-ms": "", + "label-ns": "", "milliseconds": "", - "nanoseconds": "" + "nanoseconds": "", + "tooltip": "" + }, + "tooltip": { + "disable-highlighting": "", + "download": "", + "enable-highlighting": "", + "filter-level": "" }, "unwrap-lines": "Desajustar líneas", "wrap-lines": "Ajustar líneas" @@ -11355,7 +11385,11 @@ }, "folder-repository-list": { "no-results-matching-your-query": "No hay resultados que coincidan con tu consulta", - "placeholder-search": "Buscar" + "placeholder-search": "Buscar", + "all-resources-managed_one": "", + "all-resources-managed_other": "", + "partial-managed_one": "", + "partial-managed_other": "" }, "get-default-values": { "title": { diff --git a/public/locales/fr-FR/grafana.json b/public/locales/fr-FR/grafana.json index eec2d831c88..87c4f491a33 100644 --- a/public/locales/fr-FR/grafana.json +++ b/public/locales/fr-FR/grafana.json @@ -9633,10 +9633,9 @@ } }, "logs-controls": { + "collapse": "", "deduplication": "Déduplication", - "disable-highlighting": "Désactiver la surbrillance", "disable-prettify-json": "Réduire les journaux JSON", - "display-level": "Afficher les niveaux", "display-level-all": "Tous les niveaux", "download": "Télécharger les journaux", "download-logs": { @@ -9644,18 +9643,39 @@ "json": "json", "txt": "txt" }, - "enable-highlighting": "Activer la surbrillance", "escape-newlines": "Correction des séquences de sauts de ligne et de tabulations incorrectement échappées dans les lignes du journal", - "font-size-default": "Utiliser une petite taille de police", - "font-size-small": "Utiliser la taille de police par défaut", + "expand": "", + "filter-levels": "", + "font-large": "", + "font-small": "", "hide-search": "Fermer la recherche", "hide-timestamps": "Masquer les horodatages", "hide-unique-labels": "Masquer les étiquettes uniques", + "label": { + "collapse": "", + "disable-highlighting": "", + "enable-highlighting": "", + "escape-newlines": "", + "expand": "" + }, + "labels": { + "font-large": "", + "font-small": "", + "hide-search": "", + "newest-first": "", + "oldest-first": "", + "show-search": "" + }, "line-wrapping": { "enable": "", "enable-prettify": "", "hide": "", - "label": "" + "state": { + "hide": "", + "json": "", + "wrap": "" + }, + "tooltip": "" }, "newest-first": "Trié par les journaux les plus récents en premier - Cliquez pour afficher les plus anciens en premier", "oldest-first": "Trié par les journaux les plus anciens en premier - Cliquez pour afficher les plus récents en premier", @@ -9671,8 +9691,18 @@ "timestamp": { "hide": "", "label": "", + "label-hide": "", + "label-ms": "", + "label-ns": "", "milliseconds": "", - "nanoseconds": "" + "nanoseconds": "", + "tooltip": "" + }, + "tooltip": { + "disable-highlighting": "", + "download": "", + "enable-highlighting": "", + "filter-level": "" }, "unwrap-lines": "Dérouler les lignes", "wrap-lines": "Enrouler les lignes" @@ -11355,7 +11385,11 @@ }, "folder-repository-list": { "no-results-matching-your-query": "Aucun résultat ne correspond à votre requête", - "placeholder-search": "Rechercher" + "placeholder-search": "Rechercher", + "all-resources-managed_one": "", + "all-resources-managed_other": "", + "partial-managed_one": "", + "partial-managed_other": "" }, "get-default-values": { "title": { diff --git a/public/locales/hu-HU/grafana.json b/public/locales/hu-HU/grafana.json index bfdb13e2708..291d5967b6e 100644 --- a/public/locales/hu-HU/grafana.json +++ b/public/locales/hu-HU/grafana.json @@ -9633,10 +9633,9 @@ } }, "logs-controls": { + "collapse": "", "deduplication": "Duplikálás megszüntetése", - "disable-highlighting": "Kiemelés letiltása", "disable-prettify-json": "JSON-naplók összecsukása", - "display-level": "Szintek megjelenítése", "display-level-all": "Minden szint", "download": "Naplók letöltése", "download-logs": { @@ -9644,18 +9643,39 @@ "json": "json", "txt": "txt" }, - "enable-highlighting": "Kiemelés engedélyezése", "escape-newlines": "Helytelenül értelmezett újsor- és tabulátorkarakteres szekvenciák javítása a naplósorokban", - "font-size-default": "Kis betűméret használata", - "font-size-small": "Alapértelmezett betűméret használata", + "expand": "", + "filter-levels": "", + "font-large": "", + "font-small": "", "hide-search": "Keresés bezárása", "hide-timestamps": "Időbélyegek elrejtése", "hide-unique-labels": "Egyedi címkék elrejtése", + "label": { + "collapse": "", + "disable-highlighting": "", + "enable-highlighting": "", + "escape-newlines": "", + "expand": "" + }, + "labels": { + "font-large": "", + "font-small": "", + "hide-search": "", + "newest-first": "", + "oldest-first": "", + "show-search": "" + }, "line-wrapping": { "enable": "", "enable-prettify": "", "hide": "", - "label": "" + "state": { + "hide": "", + "json": "", + "wrap": "" + }, + "tooltip": "" }, "newest-first": "Rendezés a legújabb naplók szerint – kattintson, hogy a legrégebbi naplók jelenjenek meg elsőként", "oldest-first": "Rendezés a legrégebbi naplók szerint – kattintson, hogy a legújabb naplók jelenjenek meg elsőként", @@ -9671,8 +9691,18 @@ "timestamp": { "hide": "", "label": "", + "label-hide": "", + "label-ms": "", + "label-ns": "", "milliseconds": "", - "nanoseconds": "" + "nanoseconds": "", + "tooltip": "" + }, + "tooltip": { + "disable-highlighting": "", + "download": "", + "enable-highlighting": "", + "filter-level": "" }, "unwrap-lines": "Sortörés megszüntetése", "wrap-lines": "Sortörés" @@ -11355,7 +11385,11 @@ }, "folder-repository-list": { "no-results-matching-your-query": "Nincs találat a lekérdezésre", - "placeholder-search": "Keresés" + "placeholder-search": "Keresés", + "all-resources-managed_one": "", + "all-resources-managed_other": "", + "partial-managed_one": "", + "partial-managed_other": "" }, "get-default-values": { "title": { diff --git a/public/locales/id-ID/grafana.json b/public/locales/id-ID/grafana.json index f36f2d86a5f..a4d08d24cb8 100644 --- a/public/locales/id-ID/grafana.json +++ b/public/locales/id-ID/grafana.json @@ -9601,10 +9601,9 @@ } }, "logs-controls": { + "collapse": "", "deduplication": "Deduplikasi", - "disable-highlighting": "Nonaktifkan penyorotan", "disable-prettify-json": "Ciutkan log JSON", - "display-level": "Tampilkan level", "display-level-all": "Semua level", "download": "Unduh log", "download-logs": { @@ -9612,18 +9611,39 @@ "json": "json", "txt": "txt" }, - "enable-highlighting": "Aktifkan penyorotan", "escape-newlines": "Perbaiki escape sequence baris baru dan tab yang salah di baris log", - "font-size-default": "Gunakan ukuran fon kecil", - "font-size-small": "Gunakan ukuran fon default", + "expand": "", + "filter-levels": "", + "font-large": "", + "font-small": "", "hide-search": "Tutup pencarian", "hide-timestamps": "Sembunyikan stempel waktu", "hide-unique-labels": "Sembunyikan label unik", + "label": { + "collapse": "", + "disable-highlighting": "", + "enable-highlighting": "", + "escape-newlines": "", + "expand": "" + }, + "labels": { + "font-large": "", + "font-small": "", + "hide-search": "", + "newest-first": "", + "oldest-first": "", + "show-search": "" + }, "line-wrapping": { "enable": "", "enable-prettify": "", "hide": "", - "label": "" + "state": { + "hide": "", + "json": "", + "wrap": "" + }, + "tooltip": "" }, "newest-first": "Diurutkan berdasarkan log terbaru lebih dulu - Klik untuk menampilkan yang paling lama lebih dulu", "oldest-first": "Diurutkan berdasarkan log paling lama lebih dulu - Klik untuk menampilkan yang terbaru lebih dulu", @@ -9639,8 +9659,18 @@ "timestamp": { "hide": "", "label": "", + "label-hide": "", + "label-ms": "", + "label-ns": "", "milliseconds": "", - "nanoseconds": "" + "nanoseconds": "", + "tooltip": "" + }, + "tooltip": { + "disable-highlighting": "", + "download": "", + "enable-highlighting": "", + "filter-level": "" }, "unwrap-lines": "Batal terapkan wrap pada baris", "wrap-lines": "Terapkan wrap pada baris" @@ -11316,7 +11346,9 @@ }, "folder-repository-list": { "no-results-matching-your-query": "Tidak ada hasil yang cocok dengan kueri Anda", - "placeholder-search": "Cari" + "placeholder-search": "Cari", + "all-resources-managed_other": "", + "partial-managed_other": "" }, "get-default-values": { "title": { diff --git a/public/locales/it-IT/grafana.json b/public/locales/it-IT/grafana.json index 88b4480c1e7..56fa6d29283 100644 --- a/public/locales/it-IT/grafana.json +++ b/public/locales/it-IT/grafana.json @@ -9633,10 +9633,9 @@ } }, "logs-controls": { + "collapse": "", "deduplication": "Deduplicazione", - "disable-highlighting": "Disabilita evidenziazione", "disable-prettify-json": "Riduci i registri JSON", - "display-level": "Visualizza livelli", "display-level-all": "Tutti i livelli", "download": "Scarica i registri", "download-logs": { @@ -9644,18 +9643,39 @@ "json": "json", "txt": "txt" }, - "enable-highlighting": "Abilita evidenziazione", "escape-newlines": "Correggi le sequenze di nuove righe e tabulazioni non corrette nelle righe del registro", - "font-size-default": "Usa dimensione carattere piccola", - "font-size-small": "Usa dimensione carattere predefinita", + "expand": "", + "filter-levels": "", + "font-large": "", + "font-small": "", "hide-search": "Chiudi ricerca", "hide-timestamps": "Nascondi marca temporale", "hide-unique-labels": "Nascondi etichette univoche", + "label": { + "collapse": "", + "disable-highlighting": "", + "enable-highlighting": "", + "escape-newlines": "", + "expand": "" + }, + "labels": { + "font-large": "", + "font-small": "", + "hide-search": "", + "newest-first": "", + "oldest-first": "", + "show-search": "" + }, "line-wrapping": { "enable": "", "enable-prettify": "", "hide": "", - "label": "" + "state": { + "hide": "", + "json": "", + "wrap": "" + }, + "tooltip": "" }, "newest-first": "Ordine: prima i registri più recenti – Fai clic per mostrare prima i meno recenti", "oldest-first": "Ordine: prima i registri meno recenti - Fai clic per mostrare prima i più recenti", @@ -9671,8 +9691,18 @@ "timestamp": { "hide": "", "label": "", + "label-hide": "", + "label-ms": "", + "label-ns": "", "milliseconds": "", - "nanoseconds": "" + "nanoseconds": "", + "tooltip": "" + }, + "tooltip": { + "disable-highlighting": "", + "download": "", + "enable-highlighting": "", + "filter-level": "" }, "unwrap-lines": "Rimuovi a capo", "wrap-lines": "A capo" @@ -11355,7 +11385,11 @@ }, "folder-repository-list": { "no-results-matching-your-query": "Nessun risultato corrisponde alla tua query", - "placeholder-search": "Cerca" + "placeholder-search": "Cerca", + "all-resources-managed_one": "", + "all-resources-managed_other": "", + "partial-managed_one": "", + "partial-managed_other": "" }, "get-default-values": { "title": { diff --git a/public/locales/ja-JP/grafana.json b/public/locales/ja-JP/grafana.json index a756119bfb5..7038eb7ceb9 100644 --- a/public/locales/ja-JP/grafana.json +++ b/public/locales/ja-JP/grafana.json @@ -9601,10 +9601,9 @@ } }, "logs-controls": { + "collapse": "", "deduplication": "重複排除", - "disable-highlighting": "ハイライトを無効にする", "disable-prettify-json": "JSONログを折りたたむ", - "display-level": "表示レベル", "display-level-all": "すべてのレベル", "download": "ログをダウンロード", "download-logs": { @@ -9612,18 +9611,39 @@ "json": "json", "txt": "txt" }, - "enable-highlighting": "ハイライトを有効にする", "escape-newlines": "ログ行で誤ってエスケープされた改行とタブシーケンスを修正", - "font-size-default": "小さいフォントサイズを使用", - "font-size-small": "デフォルトのフォントサイズを使用", + "expand": "", + "filter-levels": "", + "font-large": "", + "font-small": "", "hide-search": "検索を閉じる", "hide-timestamps": "タイムスタンプを非表示", "hide-unique-labels": "一意のラベルを非表示", + "label": { + "collapse": "", + "disable-highlighting": "", + "enable-highlighting": "", + "escape-newlines": "", + "expand": "" + }, + "labels": { + "font-large": "", + "font-small": "", + "hide-search": "", + "newest-first": "", + "oldest-first": "", + "show-search": "" + }, "line-wrapping": { "enable": "", "enable-prettify": "", "hide": "", - "label": "" + "state": { + "hide": "", + "json": "", + "wrap": "" + }, + "tooltip": "" }, "newest-first": "最新のログ順に並び替え - クリックして最も古いログを最初に表示", "oldest-first": "古いログ順に並び替え - クリックして最新のログを最初に表示", @@ -9639,8 +9659,18 @@ "timestamp": { "hide": "", "label": "", + "label-hide": "", + "label-ms": "", + "label-ns": "", "milliseconds": "", - "nanoseconds": "" + "nanoseconds": "", + "tooltip": "" + }, + "tooltip": { + "disable-highlighting": "", + "download": "", + "enable-highlighting": "", + "filter-level": "" }, "unwrap-lines": "行の折り返しを解除", "wrap-lines": "行を折り返す" @@ -11316,7 +11346,9 @@ }, "folder-repository-list": { "no-results-matching-your-query": "クエリに一致する結果なし", - "placeholder-search": "検索" + "placeholder-search": "検索", + "all-resources-managed_other": "", + "partial-managed_other": "" }, "get-default-values": { "title": { diff --git a/public/locales/ko-KR/grafana.json b/public/locales/ko-KR/grafana.json index 391c119f6b0..716348775d4 100644 --- a/public/locales/ko-KR/grafana.json +++ b/public/locales/ko-KR/grafana.json @@ -9601,10 +9601,9 @@ } }, "logs-controls": { + "collapse": "", "deduplication": "중복 제거", - "disable-highlighting": "강조 표시 비활성화", "disable-prettify-json": "JSON 로그 접기", - "display-level": "표시 수준", "display-level-all": "모든 수준", "download": "로그 다운로드", "download-logs": { @@ -9612,18 +9611,39 @@ "json": "json", "txt": "txt" }, - "enable-highlighting": "강조 표시 활성화", "escape-newlines": "로그 줄에서 잘못된 줄 바꿈 및 탭 시퀀스 수정", - "font-size-default": "작은 글꼴 크기 사용", - "font-size-small": "기본 글꼴 크기 사용", + "expand": "", + "filter-levels": "", + "font-large": "", + "font-small": "", "hide-search": "검색 닫기", "hide-timestamps": "타임스탬프 숨기기", "hide-unique-labels": "고유 라벨 숨기기", + "label": { + "collapse": "", + "disable-highlighting": "", + "enable-highlighting": "", + "escape-newlines": "", + "expand": "" + }, + "labels": { + "font-large": "", + "font-small": "", + "hide-search": "", + "newest-first": "", + "oldest-first": "", + "show-search": "" + }, "line-wrapping": { "enable": "", "enable-prettify": "", "hide": "", - "label": "" + "state": { + "hide": "", + "json": "", + "wrap": "" + }, + "tooltip": "" }, "newest-first": "최신 로그순으로 정렬 - 클릭하여 오래된 로그순으로 표시", "oldest-first": "오래된 로그순으로 정렬 - 클릭하여 최신 로그순으로 표시", @@ -9639,8 +9659,18 @@ "timestamp": { "hide": "", "label": "", + "label-hide": "", + "label-ms": "", + "label-ns": "", "milliseconds": "", - "nanoseconds": "" + "nanoseconds": "", + "tooltip": "" + }, + "tooltip": { + "disable-highlighting": "", + "download": "", + "enable-highlighting": "", + "filter-level": "" }, "unwrap-lines": "줄 바꿈 제거", "wrap-lines": "줄 바꿈" @@ -11316,7 +11346,9 @@ }, "folder-repository-list": { "no-results-matching-your-query": "쿼리와 일치하는 결과가 없습니다", - "placeholder-search": "검색" + "placeholder-search": "검색", + "all-resources-managed_other": "", + "partial-managed_other": "" }, "get-default-values": { "title": { diff --git a/public/locales/nl-NL/grafana.json b/public/locales/nl-NL/grafana.json index c546246a335..99c5750c4a0 100644 --- a/public/locales/nl-NL/grafana.json +++ b/public/locales/nl-NL/grafana.json @@ -9633,10 +9633,9 @@ } }, "logs-controls": { + "collapse": "", "deduplication": "Dedupliceren", - "disable-highlighting": "Markeren uitschakelen", "disable-prettify-json": "JSON-logs samenvouwen", - "display-level": "Niveaus weergeven", "display-level-all": "Alle niveaus", "download": "Logs downloaden", "download-logs": { @@ -9644,18 +9643,39 @@ "json": "json", "txt": "txt" }, - "enable-highlighting": "Markeren inschakelen", "escape-newlines": "Corrigeer onjuist escaped nieuwe lijn en tabbladsequenties in logregels", - "font-size-default": "Gebruik kleine lettergrootte", - "font-size-small": "Gebruik standaard lettergrootte", + "expand": "", + "filter-levels": "", + "font-large": "", + "font-small": "", "hide-search": "Zoekopdracht sluiten", "hide-timestamps": "Tijdstempels verbergen", "hide-unique-labels": "Unieke labels verbergen", + "label": { + "collapse": "", + "disable-highlighting": "", + "enable-highlighting": "", + "escape-newlines": "", + "expand": "" + }, + "labels": { + "font-large": "", + "font-small": "", + "hide-search": "", + "newest-first": "", + "oldest-first": "", + "show-search": "" + }, "line-wrapping": { "enable": "", "enable-prettify": "", "hide": "", - "label": "" + "state": { + "hide": "", + "json": "", + "wrap": "" + }, + "tooltip": "" }, "newest-first": "Gesorteerd op nieuwste logboeken eerst - klik om oudste eerst weer te geven", "oldest-first": "Gesorteerd op oudste logboeken eerst - klik om nieuwste eerst weer te geven", @@ -9671,8 +9691,18 @@ "timestamp": { "hide": "", "label": "", + "label-hide": "", + "label-ms": "", + "label-ns": "", "milliseconds": "", - "nanoseconds": "" + "nanoseconds": "", + "tooltip": "" + }, + "tooltip": { + "disable-highlighting": "", + "download": "", + "enable-highlighting": "", + "filter-level": "" }, "unwrap-lines": "Lijnen omsluiten", "wrap-lines": "Lijnen omsluiten" @@ -11355,7 +11385,11 @@ }, "folder-repository-list": { "no-results-matching-your-query": "Geen resultaten die overeenkomen met je query", - "placeholder-search": "Zoeken" + "placeholder-search": "Zoeken", + "all-resources-managed_one": "", + "all-resources-managed_other": "", + "partial-managed_one": "", + "partial-managed_other": "" }, "get-default-values": { "title": { diff --git a/public/locales/pl-PL/grafana.json b/public/locales/pl-PL/grafana.json index f2a4451488a..0bfb3a5f740 100644 --- a/public/locales/pl-PL/grafana.json +++ b/public/locales/pl-PL/grafana.json @@ -9697,10 +9697,9 @@ } }, "logs-controls": { + "collapse": "", "deduplication": "Deduplikacja", - "disable-highlighting": "Wyłącz wyróżnianie", "disable-prettify-json": "Zwiń logi JSON", - "display-level": "Poziomy wyświewtlania", "display-level-all": "Wszystkie poziomy", "download": "Pobierz logi", "download-logs": { @@ -9708,18 +9707,39 @@ "json": "json", "txt": "txt" }, - "enable-highlighting": "Włącz wyróżnianie", "escape-newlines": "Napraw nieprawidłowe sekwencje znaków nowego wiersza i tabulacji we wpisach logów", - "font-size-default": "Użyj małego rozmiaru czcionki", - "font-size-small": "Użyj domyślnego rozmiaru czcionki", + "expand": "", + "filter-levels": "", + "font-large": "", + "font-small": "", "hide-search": "Zamknij wyszukiwanie", "hide-timestamps": "Ukryj znaczniki czasu", "hide-unique-labels": "Ukryj unikalne etykiety", + "label": { + "collapse": "", + "disable-highlighting": "", + "enable-highlighting": "", + "escape-newlines": "", + "expand": "" + }, + "labels": { + "font-large": "", + "font-small": "", + "hide-search": "", + "newest-first": "", + "oldest-first": "", + "show-search": "" + }, "line-wrapping": { "enable": "", "enable-prettify": "", "hide": "", - "label": "" + "state": { + "hide": "", + "json": "", + "wrap": "" + }, + "tooltip": "" }, "newest-first": "Sortowanie od najnowszych wpisów dziennika – kliknij, aby wyświetlić najpierw najstarsze", "oldest-first": "Sortowanie od najstarszych wpisów dziennika – kliknij, aby wyświetlić najpierw najnowsze", @@ -9735,8 +9755,18 @@ "timestamp": { "hide": "", "label": "", + "label-hide": "", + "label-ms": "", + "label-ns": "", "milliseconds": "", - "nanoseconds": "" + "nanoseconds": "", + "tooltip": "" + }, + "tooltip": { + "disable-highlighting": "", + "download": "", + "enable-highlighting": "", + "filter-level": "" }, "unwrap-lines": "Nie zawijaj wierszy", "wrap-lines": "Zawijaj wiersze" @@ -11433,7 +11463,15 @@ }, "folder-repository-list": { "no-results-matching-your-query": "Brak wyników pasujących do zapytania", - "placeholder-search": "Szukaj" + "placeholder-search": "Szukaj", + "all-resources-managed_one": "", + "all-resources-managed_few": "", + "all-resources-managed_many": "", + "all-resources-managed_other": "", + "partial-managed_one": "", + "partial-managed_few": "", + "partial-managed_many": "", + "partial-managed_other": "" }, "get-default-values": { "title": { diff --git a/public/locales/pt-BR/grafana.json b/public/locales/pt-BR/grafana.json index a5ec46da596..0f038706204 100644 --- a/public/locales/pt-BR/grafana.json +++ b/public/locales/pt-BR/grafana.json @@ -9633,10 +9633,9 @@ } }, "logs-controls": { + "collapse": "", "deduplication": "Desduplicação", - "disable-highlighting": "Desativar destaque", "disable-prettify-json": "Recolher logs JSON", - "display-level": "Exibir níveis", "display-level-all": "Todos os níveis", "download": "Baixar logs", "download-logs": { @@ -9644,18 +9643,39 @@ "json": "json", "txt": "txt" }, - "enable-highlighting": "Ativar destaque", "escape-newlines": "Corrigir sequências de tabulação e quebra de linha adicionadas incorretamente por escape nas linhas de log", - "font-size-default": "Usar tamanho de fonte pequeno", - "font-size-small": "Usar tamanho de fonte padrão", + "expand": "", + "filter-levels": "", + "font-large": "", + "font-small": "", "hide-search": "Fechar busca", "hide-timestamps": "Ocultar data e hora", "hide-unique-labels": "Ocultar rótulos exclusivos", + "label": { + "collapse": "", + "disable-highlighting": "", + "enable-highlighting": "", + "escape-newlines": "", + "expand": "" + }, + "labels": { + "font-large": "", + "font-small": "", + "hide-search": "", + "newest-first": "", + "oldest-first": "", + "show-search": "" + }, "line-wrapping": { "enable": "", "enable-prettify": "", "hide": "", - "label": "" + "state": { + "hide": "", + "json": "", + "wrap": "" + }, + "tooltip": "" }, "newest-first": "Organizado por logs mais recentes primeiro: clique para exibir os mais antigos primeiro", "oldest-first": "Organizado por logs mais antigos primeiro: clique para exibir os mais recentes primeiro", @@ -9671,8 +9691,18 @@ "timestamp": { "hide": "", "label": "", + "label-hide": "", + "label-ms": "", + "label-ns": "", "milliseconds": "", - "nanoseconds": "" + "nanoseconds": "", + "tooltip": "" + }, + "tooltip": { + "disable-highlighting": "", + "download": "", + "enable-highlighting": "", + "filter-level": "" }, "unwrap-lines": "Desfazer quebra de linha", "wrap-lines": "Aplicar quebra de linha" @@ -11355,7 +11385,11 @@ }, "folder-repository-list": { "no-results-matching-your-query": "Nenhum resultado corresponde à sua consulta", - "placeholder-search": "Pesquisar" + "placeholder-search": "Pesquisar", + "all-resources-managed_one": "", + "all-resources-managed_other": "", + "partial-managed_one": "", + "partial-managed_other": "" }, "get-default-values": { "title": { diff --git a/public/locales/pt-PT/grafana.json b/public/locales/pt-PT/grafana.json index 564166fd27e..e923bfe8360 100644 --- a/public/locales/pt-PT/grafana.json +++ b/public/locales/pt-PT/grafana.json @@ -9633,10 +9633,9 @@ } }, "logs-controls": { + "collapse": "", "deduplication": "Desduplicação", - "disable-highlighting": "Desativar destaque", "disable-prettify-json": "Recolher registos JSON", - "display-level": "Níveis de exibição", "display-level-all": "Todos os níveis", "download": "Transferir registos", "download-logs": { @@ -9644,18 +9643,39 @@ "json": "json", "txt": "txt" }, - "enable-highlighting": "Ativar destaque", "escape-newlines": "Corrigir sequências de novas linhas e tabulações ignoradas incorretamente nas linhas de registo", - "font-size-default": "Utilize um tamanho de letra pequeno", - "font-size-small": "Utilize o tamanho de letra predefinido", + "expand": "", + "filter-levels": "", + "font-large": "", + "font-small": "", "hide-search": "Fechar a pesquisa", "hide-timestamps": "Ocultar registos de hora/data", "hide-unique-labels": "Ocultar etiquetas únicas", + "label": { + "collapse": "", + "disable-highlighting": "", + "enable-highlighting": "", + "escape-newlines": "", + "expand": "" + }, + "labels": { + "font-large": "", + "font-small": "", + "hide-search": "", + "newest-first": "", + "oldest-first": "", + "show-search": "" + }, "line-wrapping": { "enable": "", "enable-prettify": "", "hide": "", - "label": "" + "state": { + "hide": "", + "json": "", + "wrap": "" + }, + "tooltip": "" }, "newest-first": "Ordenado por registos mais recentes primeiro - Clique para mostrar os mais antigos primeiro", "oldest-first": "Ordenado por registos mais antigos primeiro - Clique para mostrar os mais recentes primeiro", @@ -9671,8 +9691,18 @@ "timestamp": { "hide": "", "label": "", + "label-hide": "", + "label-ms": "", + "label-ns": "", "milliseconds": "", - "nanoseconds": "" + "nanoseconds": "", + "tooltip": "" + }, + "tooltip": { + "disable-highlighting": "", + "download": "", + "enable-highlighting": "", + "filter-level": "" }, "unwrap-lines": "Revelar linhas", "wrap-lines": "Quebra de linhas" @@ -11355,7 +11385,11 @@ }, "folder-repository-list": { "no-results-matching-your-query": "Não foram encontrados resultados que correspondam à sua consulta", - "placeholder-search": "Pesquisar" + "placeholder-search": "Pesquisar", + "all-resources-managed_one": "", + "all-resources-managed_other": "", + "partial-managed_one": "", + "partial-managed_other": "" }, "get-default-values": { "title": { diff --git a/public/locales/ru-RU/grafana.json b/public/locales/ru-RU/grafana.json index b473fe13523..39470c31c70 100644 --- a/public/locales/ru-RU/grafana.json +++ b/public/locales/ru-RU/grafana.json @@ -9697,10 +9697,9 @@ } }, "logs-controls": { + "collapse": "", "deduplication": "Дедупликация", - "disable-highlighting": "Отключить выделение", "disable-prettify-json": "Свернуть журналы JSON", - "display-level": "Показать уровни", "display-level-all": "Все уровни", "download": "Загрузить журналы", "download-logs": { @@ -9708,18 +9707,39 @@ "json": "JSON", "txt": "TXT" }, - "enable-highlighting": "Включить выделение", "escape-newlines": "Исправить неправильно экранированные последовательности новой строки и табуляции в строках журнала", - "font-size-default": "Использовать мелкий размер шрифта", - "font-size-small": "Использовать размер шрифта по умолчанию", + "expand": "", + "filter-levels": "", + "font-large": "", + "font-small": "", "hide-search": "Закрыть поиск", "hide-timestamps": "Скрыть метки времени", "hide-unique-labels": "Скрыть уникальные метки", + "label": { + "collapse": "", + "disable-highlighting": "", + "enable-highlighting": "", + "escape-newlines": "", + "expand": "" + }, + "labels": { + "font-large": "", + "font-small": "", + "hide-search": "", + "newest-first": "", + "oldest-first": "", + "show-search": "" + }, "line-wrapping": { "enable": "", "enable-prettify": "", "hide": "", - "label": "" + "state": { + "hide": "", + "json": "", + "wrap": "" + }, + "tooltip": "" }, "newest-first": "Сначала отображаются самые новые журналы. Нажмите, чтобы показать сначала самые старые", "oldest-first": "Сначала отображаются самые старые журналы. Нажмите, чтобы показать сначала самые новые", @@ -9735,8 +9755,18 @@ "timestamp": { "hide": "", "label": "", + "label-hide": "", + "label-ms": "", + "label-ns": "", "milliseconds": "", - "nanoseconds": "" + "nanoseconds": "", + "tooltip": "" + }, + "tooltip": { + "disable-highlighting": "", + "download": "", + "enable-highlighting": "", + "filter-level": "" }, "unwrap-lines": "Не переносить строки", "wrap-lines": "Переносить строки" @@ -11433,7 +11463,15 @@ }, "folder-repository-list": { "no-results-matching-your-query": "По вашему запросу ничего не найдено", - "placeholder-search": "Поиск" + "placeholder-search": "Поиск", + "all-resources-managed_one": "", + "all-resources-managed_few": "", + "all-resources-managed_many": "", + "all-resources-managed_other": "", + "partial-managed_one": "", + "partial-managed_few": "", + "partial-managed_many": "", + "partial-managed_other": "" }, "get-default-values": { "title": { diff --git a/public/locales/sv-SE/grafana.json b/public/locales/sv-SE/grafana.json index 881f152769a..b46158b344e 100644 --- a/public/locales/sv-SE/grafana.json +++ b/public/locales/sv-SE/grafana.json @@ -9633,10 +9633,9 @@ } }, "logs-controls": { + "collapse": "", "deduplication": "Avduplicering", - "disable-highlighting": "Inaktivera markering", "disable-prettify-json": "Dölj JSON-loggar", - "display-level": "Visa nivåer", "display-level-all": "Alla nivåer", "download": "Ladda ner loggar", "download-logs": { @@ -9644,18 +9643,39 @@ "json": "json", "txt": "txt" }, - "enable-highlighting": "Aktivera markering", "escape-newlines": "Åtgärda felaktiga undantagstecken för radbrytningar och tabbar i loggrader", - "font-size-default": "Använd liten teckenstorlek", - "font-size-small": "Använd standardteckenstorlek", + "expand": "", + "filter-levels": "", + "font-large": "", + "font-small": "", "hide-search": "Stäng sökning", "hide-timestamps": "Dölj tidsstämplar", "hide-unique-labels": "Dölj unika etiketter", + "label": { + "collapse": "", + "disable-highlighting": "", + "enable-highlighting": "", + "escape-newlines": "", + "expand": "" + }, + "labels": { + "font-large": "", + "font-small": "", + "hide-search": "", + "newest-first": "", + "oldest-first": "", + "show-search": "" + }, "line-wrapping": { "enable": "", "enable-prettify": "", "hide": "", - "label": "" + "state": { + "hide": "", + "json": "", + "wrap": "" + }, + "tooltip": "" }, "newest-first": "Sorterat efter nyaste loggar först – klicka om du vill visa äldsta först", "oldest-first": "Sorterat efter äldsta loggar först – klicka om du vill visa nyaste först", @@ -9671,8 +9691,18 @@ "timestamp": { "hide": "", "label": "", + "label-hide": "", + "label-ms": "", + "label-ns": "", "milliseconds": "", - "nanoseconds": "" + "nanoseconds": "", + "tooltip": "" + }, + "tooltip": { + "disable-highlighting": "", + "download": "", + "enable-highlighting": "", + "filter-level": "" }, "unwrap-lines": "Ta bort radbrytningar", "wrap-lines": "Radbryt linjer" @@ -11355,7 +11385,11 @@ }, "folder-repository-list": { "no-results-matching-your-query": "Inga resultat som matchar din fråga", - "placeholder-search": "Sök" + "placeholder-search": "Sök", + "all-resources-managed_one": "", + "all-resources-managed_other": "", + "partial-managed_one": "", + "partial-managed_other": "" }, "get-default-values": { "title": { diff --git a/public/locales/tr-TR/grafana.json b/public/locales/tr-TR/grafana.json index bcb4f372011..f4b26202113 100644 --- a/public/locales/tr-TR/grafana.json +++ b/public/locales/tr-TR/grafana.json @@ -9633,10 +9633,9 @@ } }, "logs-controls": { + "collapse": "", "deduplication": "Yinelenenleri kaldırma", - "disable-highlighting": "Vurgulamayı devre dışı bırak", "disable-prettify-json": "JSON günlük kayıtlarını daralt", - "display-level": "Seviyeleri göster", "display-level-all": "Tüm seviyeler", "download": "Günlükleri indir", "download-logs": { @@ -9644,18 +9643,39 @@ "json": "json", "txt": "txt" }, - "enable-highlighting": "Vurgulamayı etkinleştir", "escape-newlines": "Günlük kaydı satırlarındaki kaçış karakterli satır sonu ve sekme dizilerini düzelt", - "font-size-default": "Küçük yazı tipi boyutu kullan", - "font-size-small": "Varsayılan yazı tipi boyutunu kullan", + "expand": "", + "filter-levels": "", + "font-large": "", + "font-small": "", "hide-search": "Aramayı kapat", "hide-timestamps": "Zaman damgalarını gizle", "hide-unique-labels": "Benzersiz etiketleri gizle", + "label": { + "collapse": "", + "disable-highlighting": "", + "enable-highlighting": "", + "escape-newlines": "", + "expand": "" + }, + "labels": { + "font-large": "", + "font-small": "", + "hide-search": "", + "newest-first": "", + "oldest-first": "", + "show-search": "" + }, "line-wrapping": { "enable": "", "enable-prettify": "", "hide": "", - "label": "" + "state": { + "hide": "", + "json": "", + "wrap": "" + }, + "tooltip": "" }, "newest-first": "Günlükler yeniden eskiye sıralandı: En eskileri göstermek için tıklayın", "oldest-first": "Günlükler eskiden yeniye sıralandı: En yenileri göstermek için tıklayın", @@ -9671,8 +9691,18 @@ "timestamp": { "hide": "", "label": "", + "label-hide": "", + "label-ms": "", + "label-ns": "", "milliseconds": "", - "nanoseconds": "" + "nanoseconds": "", + "tooltip": "" + }, + "tooltip": { + "disable-highlighting": "", + "download": "", + "enable-highlighting": "", + "filter-level": "" }, "unwrap-lines": "Satırları çöz", "wrap-lines": "Satırları kaydır" @@ -11355,7 +11385,11 @@ }, "folder-repository-list": { "no-results-matching-your-query": "Sorgunuzla eşleşen sonuç yok", - "placeholder-search": "Ara" + "placeholder-search": "Ara", + "all-resources-managed_one": "", + "all-resources-managed_other": "", + "partial-managed_one": "", + "partial-managed_other": "" }, "get-default-values": { "title": { diff --git a/public/locales/zh-Hans/grafana.json b/public/locales/zh-Hans/grafana.json index fb2aa1cb48b..e298bda7761 100644 --- a/public/locales/zh-Hans/grafana.json +++ b/public/locales/zh-Hans/grafana.json @@ -9601,10 +9601,9 @@ } }, "logs-controls": { + "collapse": "", "deduplication": "去重", - "disable-highlighting": "禁用突出显示", "disable-prettify-json": "收起 JSON 日志", - "display-level": "显示级别", "display-level-all": "所有级别", "download": "下载日志", "download-logs": { @@ -9612,18 +9611,39 @@ "json": "json", "txt": "txt" }, - "enable-highlighting": "启用突出显示", "escape-newlines": "修复日志行中错误转义的换行和制表符序列", - "font-size-default": "使用小字号", - "font-size-small": "使用默认字号", + "expand": "", + "filter-levels": "", + "font-large": "", + "font-small": "", "hide-search": "关闭搜索", "hide-timestamps": "隐藏时间戳", "hide-unique-labels": "隐藏唯一标签", + "label": { + "collapse": "", + "disable-highlighting": "", + "enable-highlighting": "", + "escape-newlines": "", + "expand": "" + }, + "labels": { + "font-large": "", + "font-small": "", + "hide-search": "", + "newest-first": "", + "oldest-first": "", + "show-search": "" + }, "line-wrapping": { "enable": "", "enable-prettify": "", "hide": "", - "label": "" + "state": { + "hide": "", + "json": "", + "wrap": "" + }, + "tooltip": "" }, "newest-first": "先显示最新日志 - 点击以先显示最旧日志", "oldest-first": "先显示最旧日志 - 点击以先显示最新日志", @@ -9639,8 +9659,18 @@ "timestamp": { "hide": "", "label": "", + "label-hide": "", + "label-ms": "", + "label-ns": "", "milliseconds": "", - "nanoseconds": "" + "nanoseconds": "", + "tooltip": "" + }, + "tooltip": { + "disable-highlighting": "", + "download": "", + "enable-highlighting": "", + "filter-level": "" }, "unwrap-lines": "取消多行显示", "wrap-lines": "多行显示" @@ -11316,7 +11346,9 @@ }, "folder-repository-list": { "no-results-matching-your-query": "没有找到与您的查询匹配的结果", - "placeholder-search": "搜索" + "placeholder-search": "搜索", + "all-resources-managed_other": "", + "partial-managed_other": "" }, "get-default-values": { "title": { diff --git a/public/locales/zh-Hant/grafana.json b/public/locales/zh-Hant/grafana.json index 874b2559929..e0eb86921fc 100644 --- a/public/locales/zh-Hant/grafana.json +++ b/public/locales/zh-Hant/grafana.json @@ -9601,10 +9601,9 @@ } }, "logs-controls": { + "collapse": "", "deduplication": "重複資料", - "disable-highlighting": "停用醒目提示", "disable-prettify-json": "收闔 JSON 紀錄", - "display-level": "顯示層級", "display-level-all": "所有層級", "download": "下載日誌", "download-logs": { @@ -9612,18 +9611,39 @@ "json": "json", "txt": "txt" }, - "enable-highlighting": "啟用醒目提示", "escape-newlines": "修復紀錄行中新行和分頁序列的錯誤轉義", - "font-size-default": "使用較小的字型", - "font-size-small": "使用預設的字型大小", + "expand": "", + "filter-levels": "", + "font-large": "", + "font-small": "", "hide-search": "關閉搜尋", "hide-timestamps": "隱藏時間戳記", "hide-unique-labels": "隱藏唯一標籤", + "label": { + "collapse": "", + "disable-highlighting": "", + "enable-highlighting": "", + "escape-newlines": "", + "expand": "" + }, + "labels": { + "font-large": "", + "font-small": "", + "hide-search": "", + "newest-first": "", + "oldest-first": "", + "show-search": "" + }, "line-wrapping": { "enable": "", "enable-prettify": "", "hide": "", - "label": "" + "state": { + "hide": "", + "json": "", + "wrap": "" + }, + "tooltip": "" }, "newest-first": "按最新紀錄排序 - 按一下以顯示最舊紀錄", "oldest-first": "按最舊紀錄排序 - 按一下以顯示最新紀錄", @@ -9639,8 +9659,18 @@ "timestamp": { "hide": "", "label": "", + "label-hide": "", + "label-ms": "", + "label-ns": "", "milliseconds": "", - "nanoseconds": "" + "nanoseconds": "", + "tooltip": "" + }, + "tooltip": { + "disable-highlighting": "", + "download": "", + "enable-highlighting": "", + "filter-level": "" }, "unwrap-lines": "取消換行", "wrap-lines": "換行" @@ -11316,7 +11346,9 @@ }, "folder-repository-list": { "no-results-matching-your-query": "沒有符合您查詢的結果", - "placeholder-search": "搜尋" + "placeholder-search": "搜尋", + "all-resources-managed_other": "", + "partial-managed_other": "" }, "get-default-values": { "title": { From 6c35bb2c6e23e9b0911a3ce99192f8a429594894 Mon Sep 17 00:00:00 2001 From: Gabriel MABILLE Date: Mon, 15 Sep 2025 10:11:20 +0200 Subject: [PATCH 17/33] ExtSvcAccount: Remove expensive `extsvc_total` metric (#111031) * ExtSvcAccount: Remove expensive `extsvc_total` metric * Remove unused variables --- .../serviceaccounts/extsvcaccounts/metrics.go | 31 +------------------ .../serviceaccounts/extsvcaccounts/service.go | 2 +- .../extsvcaccounts/service_test.go | 2 +- 3 files changed, 3 insertions(+), 32 deletions(-) diff --git a/pkg/services/serviceaccounts/extsvcaccounts/metrics.go b/pkg/services/serviceaccounts/extsvcaccounts/metrics.go index 249d68d564a..0503576ad00 100644 --- a/pkg/services/serviceaccounts/extsvcaccounts/metrics.go +++ b/pkg/services/serviceaccounts/extsvcaccounts/metrics.go @@ -1,45 +1,17 @@ package extsvcaccounts import ( - "context" - "time" - - "github.com/grafana/grafana/pkg/infra/log" - "github.com/grafana/grafana/pkg/services/serviceaccounts" "github.com/prometheus/client_golang/prometheus" ) type metrics struct { - storedCount prometheus.GaugeFunc savedCount prometheus.Counter deletedCount prometheus.Counter } -func newMetrics(reg prometheus.Registerer, defaultOrgID int64, saSvc serviceaccounts.Service, logger log.Logger) *metrics { +func newMetrics(reg prometheus.Registerer) *metrics { var m metrics - m.storedCount = prometheus.NewGaugeFunc( - prometheus.GaugeOpts{ - Namespace: metricsNamespace, - Name: "extsvc_total", - Help: "Number of external service accounts in store", - }, - func() float64 { - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - res, err := saSvc.SearchOrgServiceAccounts(ctx, &serviceaccounts.SearchOrgServiceAccountsQuery{ - OrgID: defaultOrgID, - Filter: serviceaccounts.FilterOnlyExternal, - CountOnly: true, - SignedInUser: extsvcuser(defaultOrgID), - }) - if err != nil { - logger.Error("Could not compute extsvc_total metric", "error", err) - return 0.0 - } - return float64(res.TotalCount) - }, - ) m.savedCount = prometheus.NewCounter(prometheus.CounterOpts{ Namespace: metricsNamespace, Name: "extsvc_saved_total", @@ -52,7 +24,6 @@ func newMetrics(reg prometheus.Registerer, defaultOrgID int64, saSvc serviceacco }) if reg != nil { - reg.MustRegister(m.storedCount) reg.MustRegister(m.savedCount) reg.MustRegister(m.deletedCount) } diff --git a/pkg/services/serviceaccounts/extsvcaccounts/service.go b/pkg/services/serviceaccounts/extsvcaccounts/service.go index c27b8aadb85..93d7087e6a1 100644 --- a/pkg/services/serviceaccounts/extsvcaccounts/service.go +++ b/pkg/services/serviceaccounts/extsvcaccounts/service.go @@ -50,7 +50,7 @@ func ProvideExtSvcAccountsService(acSvc ac.Service, cfg *setting.Cfg, bus bus.Bu if esa.enabled { // Register the metrics - esa.metrics = newMetrics(reg, esa.defaultOrgID, saSvc, logger) + esa.metrics = newMetrics(reg) // Register a listener to enable/disable service accounts bus.AddEventListener(esa.handlePluginStateChanged) diff --git a/pkg/services/serviceaccounts/extsvcaccounts/service_test.go b/pkg/services/serviceaccounts/extsvcaccounts/service_test.go index 8dde8f430c5..8bc57d8b256 100644 --- a/pkg/services/serviceaccounts/extsvcaccounts/service_test.go +++ b/pkg/services/serviceaccounts/extsvcaccounts/service_test.go @@ -58,7 +58,7 @@ func setupTestEnv(t *testing.T) *TestEnv { permreg.ProvidePermissionRegistry(), nil), defaultOrgID: autoAssignOrgID, logger: logger, - metrics: newMetrics(nil, autoAssignOrgID, env.SaSvc, logger), + metrics: newMetrics(nil), saSvc: env.SaSvc, skvStore: env.SkvStore, tracer: tracing.InitializeTracerForTest(), From f73cb477cb6979f8b3abfe4f7b738e02b5259b2f Mon Sep 17 00:00:00 2001 From: Sergej-Vlasov <37613182+Sergej-Vlasov@users.noreply.github.com> Date: Mon, 15 Sep 2025 11:59:40 +0300 Subject: [PATCH 18/33] AutoGridItem: Reset repeatedPanels when disabling repeats (#111072) reset repeatedPanels when disabling repeats --- .../dashboard-scene/scene/layout-auto-grid/AutoGridItem.tsx | 4 ++++ .../scene/layout-default/DashboardGridItem.tsx | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/public/app/features/dashboard-scene/scene/layout-auto-grid/AutoGridItem.tsx b/public/app/features/dashboard-scene/scene/layout-auto-grid/AutoGridItem.tsx index 953f7b7dbb8..5a88e451fb0 100644 --- a/public/app/features/dashboard-scene/scene/layout-auto-grid/AutoGridItem.tsx +++ b/public/app/features/dashboard-scene/scene/layout-auto-grid/AutoGridItem.tsx @@ -141,6 +141,10 @@ export class AutoGridItem extends SceneObjectBase implements public setRepeatByVariable(variableName: string | undefined) { const stateUpdate: Partial = { variableName }; + if (!variableName) { + stateUpdate.repeatedPanels = undefined; + } + if (this.state.body.state.$variables) { this.state.body.setState({ $variables: undefined }); } diff --git a/public/app/features/dashboard-scene/scene/layout-default/DashboardGridItem.tsx b/public/app/features/dashboard-scene/scene/layout-default/DashboardGridItem.tsx index 271fc3bdd42..2a2c741c425 100644 --- a/public/app/features/dashboard-scene/scene/layout-default/DashboardGridItem.tsx +++ b/public/app/features/dashboard-scene/scene/layout-default/DashboardGridItem.tsx @@ -234,6 +234,10 @@ export class DashboardGridItem public setRepeatByVariable(variableName: string | undefined) { const stateUpdate: Partial = { variableName }; + if (!variableName) { + stateUpdate.repeatedPanels = undefined; + } + if (variableName && !this.state.repeatDirection) { stateUpdate.repeatDirection = 'h'; } From afc08dbbbcb5cbd6ffa05c2711f6ff6851ca12dc Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Mon, 15 Sep 2025 12:01:45 +0300 Subject: [PATCH 19/33] Chore: go.mod updates (#110957) --- apps/alerting/alertenrichment/go.mod | 1 + apps/alerting/alertenrichment/go.sum | 4 ++-- apps/alerting/notifications/go.mod | 8 ++++--- apps/alerting/notifications/go.sum | 16 ++++++++------ apps/alerting/rules/go.mod | 8 ++++--- apps/alerting/rules/go.sum | 16 ++++++++------ apps/dashboard/go.mod | 8 ++++--- apps/dashboard/go.sum | 15 +++++++------ apps/folder/go.mod | 7 +++++-- apps/folder/go.sum | 16 ++++++++------ apps/investigations/go.mod | 8 ++++--- apps/investigations/go.sum | 16 ++++++++------ apps/playlist/go.mod | 8 ++++--- apps/playlist/go.sum | 16 ++++++++------ apps/plugins/go.mod | 8 ++++--- apps/plugins/go.sum | 16 ++++++++------ apps/preferences/go.mod | 7 +++++-- apps/preferences/go.sum | 16 ++++++++------ apps/provisioning/go.mod | 8 ++++--- apps/provisioning/go.sum | 16 ++++++++------ apps/secret/go.mod | 7 +++++-- apps/secret/go.sum | 16 ++++++++------ apps/shorturl/go.mod | 8 ++++--- apps/shorturl/go.sum | 16 ++++++++------ go.mod | 8 +++---- go.sum | 18 ++++++++-------- go.work.sum | 9 ++++++++ pkg/aggregator/go.mod | 10 +++++---- pkg/aggregator/go.sum | 21 +++++++++++-------- pkg/apimachinery/go.mod | 2 +- pkg/apimachinery/go.sum | 4 ++-- pkg/apiserver/go.mod | 8 ++++--- pkg/apiserver/go.sum | 16 ++++++++------ pkg/promlib/go.mod | 7 ++++--- pkg/promlib/go.sum | 12 +++++------ .../api/validation/api_ruler_validation.go | 2 +- pkg/services/ngalert/models/alert_rule.go | 5 +++-- pkg/setting/setting.go | 2 +- 38 files changed, 240 insertions(+), 149 deletions(-) diff --git a/apps/alerting/alertenrichment/go.mod b/apps/alerting/alertenrichment/go.mod index f63cc376563..8d56ef28195 100644 --- a/apps/alerting/alertenrichment/go.mod +++ b/apps/alerting/alertenrichment/go.mod @@ -23,6 +23,7 @@ require ( github.com/mailru/easyjson v0.9.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/stretchr/testify v1.11.1 // indirect github.com/x448/float16 v0.8.4 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect diff --git a/apps/alerting/alertenrichment/go.sum b/apps/alerting/alertenrichment/go.sum index 5fa5cfd9fd4..f29c174edc7 100644 --- a/apps/alerting/alertenrichment/go.sum +++ b/apps/alerting/alertenrichment/go.sum @@ -48,8 +48,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.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= diff --git a/apps/alerting/notifications/go.mod b/apps/alerting/notifications/go.mod index b6e76e96157..61ff43d776e 100644 --- a/apps/alerting/notifications/go.mod +++ b/apps/alerting/notifications/go.mod @@ -36,6 +36,7 @@ require ( github.com/google/go-cmp v0.7.0 // indirect github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect @@ -56,13 +57,13 @@ require ( github.com/onsi/gomega v1.36.2 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_golang v1.23.0 // indirect + github.com/prometheus/client_golang v1.23.1 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.65.0 // indirect + github.com/prometheus/common v0.66.0 // indirect github.com/prometheus/procfs v0.16.1 // indirect github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect - github.com/spf13/pflag v1.0.7 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/x448/float16 v0.8.4 // indirect go.etcd.io/bbolt v1.4.0 // indirect go.etcd.io/etcd/api/v3 v3.5.21 // indirect @@ -98,6 +99,7 @@ require ( google.golang.org/protobuf v1.36.8 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/api v0.33.3 // indirect k8s.io/apiextensions-apiserver v0.33.3 // indirect diff --git a/apps/alerting/notifications/go.sum b/apps/alerting/notifications/go.sum index 54b5de51bdc..ffc862de007 100644 --- a/apps/alerting/notifications/go.sum +++ b/apps/alerting/notifications/go.sum @@ -88,6 +88,8 @@ github.com/grafana/grafana-app-sdk v0.40.3 h1:JFo7uAfbAJUfZ9neD7/4sODKm1xgu9zhck github.com/grafana/grafana-app-sdk v0.40.3/go.mod h1:j0KzHo3Sa6kd+lnwSScBNoV9Vobkg/YY9HtEjxpyPrk= github.com/grafana/grafana-app-sdk/logging v0.40.3 h1:2VXsXXEQiqAavRP8wusRDB6rDqf5lufP7A6NfjELqPE= github.com/grafana/grafana-app-sdk/logging v0.40.3/go.mod h1:otUD9XpJD7A5sCLb8mcs9hIXGdeV6lnhzVwe747g4RU= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 h1:uGoIog/wiQHI9GAxXO5TJbT0wWKH3O9HhOJW1F9c3fY= @@ -151,16 +153,16 @@ 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.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= -github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= +github.com/prometheus/client_golang v1.23.1 h1:w6gXMLQGgd0jXXlote9lRHMe0nG01EbnJT+C0EJru2Y= +github.com/prometheus/client_golang v1.23.1/go.mod h1:br8j//v2eg2K5Vvna5klK8Ku5pcU5r4ll73v6ik5dIQ= 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= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= -github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/common v0.66.0 h1:K/rJPHrG3+AoQs50r2+0t7zMnMzek2Vbv31OFVsMeVY= +github.com/prometheus/common v0.66.0/go.mod h1:Ux6NtV1B4LatamKE63tJBntoxD++xmtI/lK0VtEplN4= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= @@ -173,8 +175,8 @@ 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/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= @@ -351,6 +353,8 @@ gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/apps/alerting/rules/go.mod b/apps/alerting/rules/go.mod index 529c8dc68e1..eb8e64deadc 100644 --- a/apps/alerting/rules/go.mod +++ b/apps/alerting/rules/go.mod @@ -30,6 +30,7 @@ require ( github.com/google/go-cmp v0.7.0 // indirect github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect @@ -45,13 +46,13 @@ require ( github.com/onsi/ginkgo/v2 v2.22.2 // indirect github.com/onsi/gomega v1.36.2 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect - github.com/prometheus/client_golang v1.23.0 // indirect + github.com/prometheus/client_golang v1.23.1 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.65.0 // indirect + github.com/prometheus/common v0.66.0 // indirect github.com/prometheus/procfs v0.16.1 // indirect github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect - github.com/spf13/pflag v1.0.7 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/otel v1.38.0 // indirect @@ -77,6 +78,7 @@ require ( google.golang.org/grpc v1.75.0 // indirect google.golang.org/protobuf v1.36.8 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/api v0.33.3 // indirect k8s.io/apiextensions-apiserver v0.33.3 // indirect diff --git a/apps/alerting/rules/go.sum b/apps/alerting/rules/go.sum index 8e94a91aa99..76a2bd7967f 100644 --- a/apps/alerting/rules/go.sum +++ b/apps/alerting/rules/go.sum @@ -51,6 +51,8 @@ github.com/grafana/grafana-app-sdk v0.40.3 h1:JFo7uAfbAJUfZ9neD7/4sODKm1xgu9zhck github.com/grafana/grafana-app-sdk v0.40.3/go.mod h1:j0KzHo3Sa6kd+lnwSScBNoV9Vobkg/YY9HtEjxpyPrk= github.com/grafana/grafana-app-sdk/logging v0.40.3 h1:2VXsXXEQiqAavRP8wusRDB6rDqf5lufP7A6NfjELqPE= github.com/grafana/grafana-app-sdk/logging v0.40.3/go.mod h1:otUD9XpJD7A5sCLb8mcs9hIXGdeV6lnhzVwe747g4RU= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -98,20 +100,20 @@ 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.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= -github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= +github.com/prometheus/client_golang v1.23.1 h1:w6gXMLQGgd0jXXlote9lRHMe0nG01EbnJT+C0EJru2Y= +github.com/prometheus/client_golang v1.23.1/go.mod h1:br8j//v2eg2K5Vvna5klK8Ku5pcU5r4ll73v6ik5dIQ= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= -github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/common v0.66.0 h1:K/rJPHrG3+AoQs50r2+0t7zMnMzek2Vbv31OFVsMeVY= +github.com/prometheus/common v0.66.0/go.mod h1:Ux6NtV1B4LatamKE63tJBntoxD++xmtI/lK0VtEplN4= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/puzpuzpuz/xsync/v2 v2.5.1 h1:mVGYAvzDSu52+zaGyNjC+24Xw2bQi3kTr4QJ6N9pIIU= github.com/puzpuzpuz/xsync/v2 v2.5.1/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= @@ -210,6 +212,8 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/api v0.33.3 h1:SRd5t//hhkI1buzxb288fy2xvjubstenEKL9K51KBI8= diff --git a/apps/dashboard/go.mod b/apps/dashboard/go.mod index 18d5ed38638..ae3e42bf657 100644 --- a/apps/dashboard/go.mod +++ b/apps/dashboard/go.mod @@ -9,7 +9,7 @@ require ( github.com/grafana/grafana-app-sdk/logging v0.40.3 github.com/grafana/grafana-plugin-sdk-go v0.278.0 github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e - github.com/prometheus/client_golang v1.23.0 + github.com/prometheus/client_golang v1.23.1 github.com/stretchr/testify v1.11.1 golang.org/x/net v0.44.0 k8s.io/apimachinery v0.33.3 @@ -56,6 +56,7 @@ require ( github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 // indirect github.com/grafana/otel-profiling-go v0.5.1 // indirect 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.1.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect @@ -91,12 +92,12 @@ require ( github.com/pierrec/lz4/v4 v4.1.22 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.65.0 // indirect + github.com/prometheus/common v0.66.0 // indirect github.com/prometheus/procfs v0.16.1 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/smartystreets/goconvey v1.8.1 // indirect - github.com/spf13/pflag v1.0.7 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/unknwon/bra v0.0.0-20200517080246-1e3013ecaff8 // indirect github.com/unknwon/com v1.0.1 // indirect @@ -136,6 +137,7 @@ require ( google.golang.org/protobuf v1.36.8 // indirect gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/client-go v0.33.3 // indirect k8s.io/component-base v0.33.3 // indirect diff --git a/apps/dashboard/go.sum b/apps/dashboard/go.sum index 900a70892a1..fc5e8091c90 100644 --- a/apps/dashboard/go.sum +++ b/apps/dashboard/go.sum @@ -116,6 +116,8 @@ github.com/grafana/otel-profiling-go v0.5.1 h1:stVPKAFZSa7eGiqbYuG25VcqYksR6iWvF github.com/grafana/otel-profiling-go v0.5.1/go.mod h1:ftN/t5A/4gQI19/8MoWurBEtC6gFw8Dns1sJZ9W4Tls= github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg= github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0= @@ -221,12 +223,12 @@ 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.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= -github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= +github.com/prometheus/client_golang v1.23.1 h1:w6gXMLQGgd0jXXlote9lRHMe0nG01EbnJT+C0EJru2Y= +github.com/prometheus/client_golang v1.23.1/go.mod h1:br8j//v2eg2K5Vvna5klK8Ku5pcU5r4ll73v6ik5dIQ= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= -github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/common v0.66.0 h1:K/rJPHrG3+AoQs50r2+0t7zMnMzek2Vbv31OFVsMeVY= +github.com/prometheus/common v0.66.0/go.mod h1:Ux6NtV1B4LatamKE63tJBntoxD++xmtI/lK0VtEplN4= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/protocolbuffers/txtpbfmt v0.0.0-20241112170944-20d2c9ebc01d h1:HWfigq7lB31IeJL8iy7jkUmU/PG1Sr8jVGhS749dbUA= @@ -246,8 +248,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.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY= github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= -github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -428,6 +430,7 @@ gopkg.in/fsnotify/fsnotify.v1 v1.4.7/go.mod h1:Fyux9zXlo4rWoMSIzpn9fDAYjalPqJ/K1 gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/apps/folder/go.mod b/apps/folder/go.mod index 08388e82581..a5f8601b278 100644 --- a/apps/folder/go.mod +++ b/apps/folder/go.mod @@ -24,6 +24,7 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/google/gnostic-models v0.6.9 // indirect github.com/grafana/grafana-app-sdk/logging v0.40.3 // indirect + github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // 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 @@ -37,10 +38,11 @@ require ( github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.23.0 // indirect + github.com/prometheus/client_golang v1.23.1 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.65.0 // indirect + github.com/prometheus/common v0.66.0 // indirect github.com/prometheus/procfs v0.16.1 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/stretchr/testify v1.11.1 // indirect github.com/x448/float16 v0.8.4 // indirect @@ -56,6 +58,7 @@ require ( golang.org/x/time v0.13.0 // indirect google.golang.org/protobuf v1.36.8 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/client-go v0.33.3 // indirect k8s.io/klog/v2 v2.130.1 // indirect diff --git a/apps/folder/go.sum b/apps/folder/go.sum index e61f142abbf..90d5e4cccba 100644 --- a/apps/folder/go.sum +++ b/apps/folder/go.sum @@ -38,6 +38,8 @@ github.com/grafana/grafana-app-sdk/logging v0.40.3 h1:2VXsXXEQiqAavRP8wusRDB6rDq github.com/grafana/grafana-app-sdk/logging v0.40.3/go.mod h1:otUD9XpJD7A5sCLb8mcs9hIXGdeV6lnhzVwe747g4RU= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e h1:BTKk7LHuG1kmAkucwTA7DuMbKpKvJTKrGdBmUNO4dfQ= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e/go.mod h1:IA4SOwun8QyST9c5UNs/fN37XL6boXXDvRYFcFwbipg= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= github.com/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= @@ -79,18 +81,18 @@ 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.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= -github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= +github.com/prometheus/client_golang v1.23.1 h1:w6gXMLQGgd0jXXlote9lRHMe0nG01EbnJT+C0EJru2Y= +github.com/prometheus/client_golang v1.23.1/go.mod h1:br8j//v2eg2K5Vvna5klK8Ku5pcU5r4ll73v6ik5dIQ= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= -github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/common v0.66.0 h1:K/rJPHrG3+AoQs50r2+0t7zMnMzek2Vbv31OFVsMeVY= +github.com/prometheus/common v0.66.0/go.mod h1:Ux6NtV1B4LatamKE63tJBntoxD++xmtI/lK0VtEplN4= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= @@ -159,6 +161,8 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/api v0.33.3 h1:SRd5t//hhkI1buzxb288fy2xvjubstenEKL9K51KBI8= diff --git a/apps/investigations/go.mod b/apps/investigations/go.mod index c9e76433875..dea9bea5fe2 100644 --- a/apps/investigations/go.mod +++ b/apps/investigations/go.mod @@ -31,6 +31,7 @@ require ( github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect github.com/google/uuid v1.6.0 // indirect github.com/grafana/grafana-app-sdk/logging v0.40.3 // indirect + github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect @@ -46,13 +47,13 @@ require ( github.com/onsi/ginkgo/v2 v2.22.2 // indirect github.com/onsi/gomega v1.36.2 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect - github.com/prometheus/client_golang v1.23.0 // indirect + github.com/prometheus/client_golang v1.23.1 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.65.0 // indirect + github.com/prometheus/common v0.66.0 // indirect github.com/prometheus/procfs v0.16.1 // indirect github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect - github.com/spf13/pflag v1.0.7 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/otel v1.38.0 // indirect @@ -78,6 +79,7 @@ require ( google.golang.org/grpc v1.75.0 // indirect google.golang.org/protobuf v1.36.8 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/api v0.33.3 // indirect k8s.io/apiextensions-apiserver v0.33.3 // indirect diff --git a/apps/investigations/go.sum b/apps/investigations/go.sum index 8e94a91aa99..76a2bd7967f 100644 --- a/apps/investigations/go.sum +++ b/apps/investigations/go.sum @@ -51,6 +51,8 @@ github.com/grafana/grafana-app-sdk v0.40.3 h1:JFo7uAfbAJUfZ9neD7/4sODKm1xgu9zhck github.com/grafana/grafana-app-sdk v0.40.3/go.mod h1:j0KzHo3Sa6kd+lnwSScBNoV9Vobkg/YY9HtEjxpyPrk= github.com/grafana/grafana-app-sdk/logging v0.40.3 h1:2VXsXXEQiqAavRP8wusRDB6rDqf5lufP7A6NfjELqPE= github.com/grafana/grafana-app-sdk/logging v0.40.3/go.mod h1:otUD9XpJD7A5sCLb8mcs9hIXGdeV6lnhzVwe747g4RU= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -98,20 +100,20 @@ 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.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= -github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= +github.com/prometheus/client_golang v1.23.1 h1:w6gXMLQGgd0jXXlote9lRHMe0nG01EbnJT+C0EJru2Y= +github.com/prometheus/client_golang v1.23.1/go.mod h1:br8j//v2eg2K5Vvna5klK8Ku5pcU5r4ll73v6ik5dIQ= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= -github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/common v0.66.0 h1:K/rJPHrG3+AoQs50r2+0t7zMnMzek2Vbv31OFVsMeVY= +github.com/prometheus/common v0.66.0/go.mod h1:Ux6NtV1B4LatamKE63tJBntoxD++xmtI/lK0VtEplN4= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/puzpuzpuz/xsync/v2 v2.5.1 h1:mVGYAvzDSu52+zaGyNjC+24Xw2bQi3kTr4QJ6N9pIIU= github.com/puzpuzpuz/xsync/v2 v2.5.1/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= @@ -210,6 +212,8 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/api v0.33.3 h1:SRd5t//hhkI1buzxb288fy2xvjubstenEKL9K51KBI8= diff --git a/apps/playlist/go.mod b/apps/playlist/go.mod index 1eb90d41986..e98a1b67deb 100644 --- a/apps/playlist/go.mod +++ b/apps/playlist/go.mod @@ -31,6 +31,7 @@ require ( github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect github.com/google/uuid v1.6.0 // indirect github.com/grafana/grafana-app-sdk/logging v0.40.3 // indirect + github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect @@ -46,13 +47,13 @@ require ( github.com/onsi/ginkgo/v2 v2.22.2 // indirect github.com/onsi/gomega v1.36.2 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect - github.com/prometheus/client_golang v1.23.0 // indirect + github.com/prometheus/client_golang v1.23.1 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.65.0 // indirect + github.com/prometheus/common v0.66.0 // indirect github.com/prometheus/procfs v0.16.1 // indirect github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect - github.com/spf13/pflag v1.0.7 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/otel v1.38.0 // indirect @@ -78,6 +79,7 @@ require ( google.golang.org/grpc v1.75.0 // indirect google.golang.org/protobuf v1.36.8 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/api v0.33.3 // indirect k8s.io/apiextensions-apiserver v0.33.3 // indirect diff --git a/apps/playlist/go.sum b/apps/playlist/go.sum index 8e94a91aa99..76a2bd7967f 100644 --- a/apps/playlist/go.sum +++ b/apps/playlist/go.sum @@ -51,6 +51,8 @@ github.com/grafana/grafana-app-sdk v0.40.3 h1:JFo7uAfbAJUfZ9neD7/4sODKm1xgu9zhck github.com/grafana/grafana-app-sdk v0.40.3/go.mod h1:j0KzHo3Sa6kd+lnwSScBNoV9Vobkg/YY9HtEjxpyPrk= github.com/grafana/grafana-app-sdk/logging v0.40.3 h1:2VXsXXEQiqAavRP8wusRDB6rDqf5lufP7A6NfjELqPE= github.com/grafana/grafana-app-sdk/logging v0.40.3/go.mod h1:otUD9XpJD7A5sCLb8mcs9hIXGdeV6lnhzVwe747g4RU= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -98,20 +100,20 @@ 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.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= -github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= +github.com/prometheus/client_golang v1.23.1 h1:w6gXMLQGgd0jXXlote9lRHMe0nG01EbnJT+C0EJru2Y= +github.com/prometheus/client_golang v1.23.1/go.mod h1:br8j//v2eg2K5Vvna5klK8Ku5pcU5r4ll73v6ik5dIQ= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= -github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/common v0.66.0 h1:K/rJPHrG3+AoQs50r2+0t7zMnMzek2Vbv31OFVsMeVY= +github.com/prometheus/common v0.66.0/go.mod h1:Ux6NtV1B4LatamKE63tJBntoxD++xmtI/lK0VtEplN4= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/puzpuzpuz/xsync/v2 v2.5.1 h1:mVGYAvzDSu52+zaGyNjC+24Xw2bQi3kTr4QJ6N9pIIU= github.com/puzpuzpuz/xsync/v2 v2.5.1/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= @@ -210,6 +212,8 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/api v0.33.3 h1:SRd5t//hhkI1buzxb288fy2xvjubstenEKL9K51KBI8= diff --git a/apps/plugins/go.mod b/apps/plugins/go.mod index e588df3e680..b5f72f7c26d 100644 --- a/apps/plugins/go.mod +++ b/apps/plugins/go.mod @@ -37,6 +37,7 @@ require ( github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43 // indirect github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 // indirect github.com/grafana/grafana-app-sdk/logging v0.40.3 // indirect + github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect @@ -53,12 +54,12 @@ require ( github.com/onsi/gomega v1.36.2 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect - github.com/prometheus/client_golang v1.23.0 // indirect + github.com/prometheus/client_golang v1.23.1 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.65.0 // indirect + github.com/prometheus/common v0.66.0 // indirect github.com/prometheus/procfs v0.16.1 // indirect github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect - github.com/spf13/pflag v1.0.7 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/otel v1.38.0 // indirect @@ -85,6 +86,7 @@ require ( google.golang.org/grpc v1.75.0 // indirect google.golang.org/protobuf v1.36.8 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/api v0.33.3 // indirect k8s.io/apiextensions-apiserver v0.33.3 // indirect diff --git a/apps/plugins/go.sum b/apps/plugins/go.sum index 8876a7257e0..6d4958058a4 100644 --- a/apps/plugins/go.sum +++ b/apps/plugins/go.sum @@ -63,6 +63,8 @@ github.com/grafana/grafana-app-sdk/logging v0.40.3 h1:2VXsXXEQiqAavRP8wusRDB6rDq github.com/grafana/grafana-app-sdk/logging v0.40.3/go.mod h1:otUD9XpJD7A5sCLb8mcs9hIXGdeV6lnhzVwe747g4RU= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250428110029-a8ea72012bde h1:ydSrBIOCxJQ84+JU+cyYsOLL40QeXrB7rYfsY/ezU4w= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250428110029-a8ea72012bde/go.mod h1:3MwgP0ISxGviTy3ZUJZsNz/56NNtHztMlH+gcxDt6Tw= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -112,20 +114,20 @@ 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.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= -github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= +github.com/prometheus/client_golang v1.23.1 h1:w6gXMLQGgd0jXXlote9lRHMe0nG01EbnJT+C0EJru2Y= +github.com/prometheus/client_golang v1.23.1/go.mod h1:br8j//v2eg2K5Vvna5klK8Ku5pcU5r4ll73v6ik5dIQ= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= -github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/common v0.66.0 h1:K/rJPHrG3+AoQs50r2+0t7zMnMzek2Vbv31OFVsMeVY= +github.com/prometheus/common v0.66.0/go.mod h1:Ux6NtV1B4LatamKE63tJBntoxD++xmtI/lK0VtEplN4= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/puzpuzpuz/xsync/v2 v2.5.1 h1:mVGYAvzDSu52+zaGyNjC+24Xw2bQi3kTr4QJ6N9pIIU= github.com/puzpuzpuz/xsync/v2 v2.5.1/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= @@ -256,6 +258,8 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/apps/preferences/go.mod b/apps/preferences/go.mod index c14bd047fac..b586ecf52f1 100644 --- a/apps/preferences/go.mod +++ b/apps/preferences/go.mod @@ -24,6 +24,7 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/google/gnostic-models v0.6.9 // indirect github.com/grafana/grafana-app-sdk/logging v0.40.3 // indirect + github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // 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 @@ -37,10 +38,11 @@ require ( github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.23.0 // indirect + github.com/prometheus/client_golang v1.23.1 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.65.0 // indirect + github.com/prometheus/common v0.66.0 // indirect github.com/prometheus/procfs v0.16.1 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/stretchr/testify v1.11.1 // indirect github.com/x448/float16 v0.8.4 // indirect @@ -56,6 +58,7 @@ require ( golang.org/x/time v0.13.0 // indirect google.golang.org/protobuf v1.36.8 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/client-go v0.33.3 // indirect k8s.io/klog/v2 v2.130.1 // indirect diff --git a/apps/preferences/go.sum b/apps/preferences/go.sum index 040ff075f8d..467a02af004 100644 --- a/apps/preferences/go.sum +++ b/apps/preferences/go.sum @@ -38,6 +38,8 @@ github.com/grafana/grafana-app-sdk/logging v0.40.3 h1:2VXsXXEQiqAavRP8wusRDB6rDq github.com/grafana/grafana-app-sdk/logging v0.40.3/go.mod h1:otUD9XpJD7A5sCLb8mcs9hIXGdeV6lnhzVwe747g4RU= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250804150913-990f1c69ecc2 h1:X0cnaFdR+iz+sDSuoZmkryFSjOirchHe2MdKSRwBWgM= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:RRvSjHH12/PnQaXraMO65jUhVu8n59mzvhfIMBETnV4= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= github.com/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= @@ -79,18 +81,18 @@ 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.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= -github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= +github.com/prometheus/client_golang v1.23.1 h1:w6gXMLQGgd0jXXlote9lRHMe0nG01EbnJT+C0EJru2Y= +github.com/prometheus/client_golang v1.23.1/go.mod h1:br8j//v2eg2K5Vvna5klK8Ku5pcU5r4ll73v6ik5dIQ= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= -github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/common v0.66.0 h1:K/rJPHrG3+AoQs50r2+0t7zMnMzek2Vbv31OFVsMeVY= +github.com/prometheus/common v0.66.0/go.mod h1:Ux6NtV1B4LatamKE63tJBntoxD++xmtI/lK0VtEplN4= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= @@ -159,6 +161,8 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/api v0.33.3 h1:SRd5t//hhkI1buzxb288fy2xvjubstenEKL9K51KBI8= diff --git a/apps/provisioning/go.mod b/apps/provisioning/go.mod index dca6b391b23..40f81709622 100644 --- a/apps/provisioning/go.mod +++ b/apps/provisioning/go.mod @@ -42,6 +42,7 @@ require ( github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43 // indirect github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 // indirect github.com/grafana/grafana-app-sdk v0.40.3 // indirect + github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.18.0 // indirect @@ -52,11 +53,11 @@ require ( github.com/patrickmn/go-cache v2.1.0+incompatible // 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.23.0 // indirect + github.com/prometheus/client_golang v1.23.1 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.65.0 // indirect + github.com/prometheus/common v0.66.0 // indirect github.com/prometheus/procfs v0.16.1 // indirect - github.com/spf13/pflag v1.0.7 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect @@ -79,6 +80,7 @@ require ( google.golang.org/protobuf v1.36.8 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/api v0.33.3 // indirect k8s.io/component-base v0.33.3 // indirect diff --git a/apps/provisioning/go.sum b/apps/provisioning/go.sum index f1f1b810347..4c00598b5de 100644 --- a/apps/provisioning/go.sum +++ b/apps/provisioning/go.sum @@ -66,6 +66,8 @@ github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250804150913-990f1c69ecc2 h github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:RRvSjHH12/PnQaXraMO65jUhVu8n59mzvhfIMBETnV4= github.com/grafana/nanogit v0.0.0-20250723104447-68f58f5ecec0 h1:cS0SlJGIlZbmDLctNj5vIYGemrJDLy25wwoiIyZWVN8= github.com/grafana/nanogit v0.0.0-20250723104447-68f58f5ecec0/go.mod h1:ToqLjIdvV3AZQa3K6e5m9hy/nsGaUByc2dWQlctB9iA= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -102,18 +104,18 @@ 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.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= -github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= +github.com/prometheus/client_golang v1.23.1 h1:w6gXMLQGgd0jXXlote9lRHMe0nG01EbnJT+C0EJru2Y= +github.com/prometheus/client_golang v1.23.1/go.mod h1:br8j//v2eg2K5Vvna5klK8Ku5pcU5r4ll73v6ik5dIQ= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= -github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/common v0.66.0 h1:K/rJPHrG3+AoQs50r2+0t7zMnMzek2Vbv31OFVsMeVY= +github.com/prometheus/common v0.66.0/go.mod h1:Ux6NtV1B4LatamKE63tJBntoxD++xmtI/lK0VtEplN4= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= @@ -230,6 +232,8 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/apps/secret/go.mod b/apps/secret/go.mod index dd0419d4bdd..3eafe5927c3 100644 --- a/apps/secret/go.mod +++ b/apps/secret/go.mod @@ -29,6 +29,7 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/google/gnostic-models v0.6.9 // indirect github.com/grafana/grafana-app-sdk/logging v0.40.3 // indirect + github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // 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 @@ -42,10 +43,11 @@ require ( github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.23.0 // indirect + github.com/prometheus/client_golang v1.23.1 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.65.0 // indirect + github.com/prometheus/common v0.66.0 // indirect github.com/prometheus/procfs v0.16.1 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/otel v1.38.0 // indirect @@ -61,6 +63,7 @@ require ( golang.org/x/time v0.13.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/client-go v0.33.3 // indirect k8s.io/klog/v2 v2.130.1 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect diff --git a/apps/secret/go.sum b/apps/secret/go.sum index b00cab6299c..0f905737469 100644 --- a/apps/secret/go.sum +++ b/apps/secret/go.sum @@ -42,6 +42,8 @@ github.com/grafana/grafana-app-sdk/logging v0.40.3 h1:2VXsXXEQiqAavRP8wusRDB6rDq github.com/grafana/grafana-app-sdk/logging v0.40.3/go.mod h1:otUD9XpJD7A5sCLb8mcs9hIXGdeV6lnhzVwe747g4RU= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250710134100-1f3dc0533caf h1:BBGDHffvVNLoYQlXEpbXcxE0vbpq7pm/8OWF5I+UDZg= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250710134100-1f3dc0533caf/go.mod h1:eAlOam2uWhrsEZlOoAr7XZ9hbBP7SyYGYn31/aQAPs8= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= github.com/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= @@ -83,18 +85,18 @@ 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.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= -github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= +github.com/prometheus/client_golang v1.23.1 h1:w6gXMLQGgd0jXXlote9lRHMe0nG01EbnJT+C0EJru2Y= +github.com/prometheus/client_golang v1.23.1/go.mod h1:br8j//v2eg2K5Vvna5klK8Ku5pcU5r4ll73v6ik5dIQ= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= -github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/common v0.66.0 h1:K/rJPHrG3+AoQs50r2+0t7zMnMzek2Vbv31OFVsMeVY= +github.com/prometheus/common v0.66.0/go.mod h1:Ux6NtV1B4LatamKE63tJBntoxD++xmtI/lK0VtEplN4= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= @@ -177,6 +179,8 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/api v0.33.3 h1:SRd5t//hhkI1buzxb288fy2xvjubstenEKL9K51KBI8= diff --git a/apps/shorturl/go.mod b/apps/shorturl/go.mod index dda67ada683..a015415473c 100644 --- a/apps/shorturl/go.mod +++ b/apps/shorturl/go.mod @@ -32,6 +32,7 @@ require ( github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect github.com/google/uuid v1.6.0 // indirect github.com/grafana/grafana-app-sdk/logging v0.40.3 // indirect + github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect @@ -47,13 +48,13 @@ require ( github.com/onsi/ginkgo/v2 v2.22.2 // indirect github.com/onsi/gomega v1.36.2 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect - github.com/prometheus/client_golang v1.23.0 // indirect + github.com/prometheus/client_golang v1.23.1 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.65.0 // indirect + github.com/prometheus/common v0.66.0 // indirect github.com/prometheus/procfs v0.16.1 // indirect github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect - github.com/spf13/pflag v1.0.7 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/otel v1.38.0 // indirect @@ -79,6 +80,7 @@ require ( google.golang.org/grpc v1.75.0 // indirect google.golang.org/protobuf v1.36.8 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/api v0.33.3 // indirect k8s.io/apiextensions-apiserver v0.33.3 // indirect diff --git a/apps/shorturl/go.sum b/apps/shorturl/go.sum index 8e94a91aa99..76a2bd7967f 100644 --- a/apps/shorturl/go.sum +++ b/apps/shorturl/go.sum @@ -51,6 +51,8 @@ github.com/grafana/grafana-app-sdk v0.40.3 h1:JFo7uAfbAJUfZ9neD7/4sODKm1xgu9zhck github.com/grafana/grafana-app-sdk v0.40.3/go.mod h1:j0KzHo3Sa6kd+lnwSScBNoV9Vobkg/YY9HtEjxpyPrk= github.com/grafana/grafana-app-sdk/logging v0.40.3 h1:2VXsXXEQiqAavRP8wusRDB6rDqf5lufP7A6NfjELqPE= github.com/grafana/grafana-app-sdk/logging v0.40.3/go.mod h1:otUD9XpJD7A5sCLb8mcs9hIXGdeV6lnhzVwe747g4RU= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -98,20 +100,20 @@ 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.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= -github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= +github.com/prometheus/client_golang v1.23.1 h1:w6gXMLQGgd0jXXlote9lRHMe0nG01EbnJT+C0EJru2Y= +github.com/prometheus/client_golang v1.23.1/go.mod h1:br8j//v2eg2K5Vvna5klK8Ku5pcU5r4ll73v6ik5dIQ= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= -github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/common v0.66.0 h1:K/rJPHrG3+AoQs50r2+0t7zMnMzek2Vbv31OFVsMeVY= +github.com/prometheus/common v0.66.0/go.mod h1:Ux6NtV1B4LatamKE63tJBntoxD++xmtI/lK0VtEplN4= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/puzpuzpuz/xsync/v2 v2.5.1 h1:mVGYAvzDSu52+zaGyNjC+24Xw2bQi3kTr4QJ6N9pIIU= github.com/puzpuzpuz/xsync/v2 v2.5.1/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= @@ -210,6 +212,8 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/api v0.33.3 h1:SRd5t//hhkI1buzxb288fy2xvjubstenEKL9K51KBI8= diff --git a/go.mod b/go.mod index ec8cc6b2e9c..afbe29920dd 100644 --- a/go.mod +++ b/go.mod @@ -155,9 +155,9 @@ require ( github.com/patrickmn/go-cache v2.1.0+incompatible // @grafana/alerting-backend github.com/phpdave11/gofpdi v1.0.14 // @grafana/sharing-squad github.com/prometheus/alertmanager v0.28.0 // @grafana/alerting-backend - github.com/prometheus/client_golang v1.23.0 // @grafana/alerting-backend + github.com/prometheus/client_golang v1.23.1 // @grafana/alerting-backend github.com/prometheus/client_model v0.6.2 // @grafana/grafana-backend-group - github.com/prometheus/common v0.65.0 // @grafana/alerting-backend + github.com/prometheus/common v0.66.0 // @grafana/alerting-backend github.com/prometheus/prometheus v0.303.1 // @grafana/alerting-backend github.com/prometheus/sigv4 v0.1.2 // @grafana/alerting-backend github.com/redis/go-redis/v9 v9.8.0 // @grafana/alerting-backend @@ -165,8 +165,8 @@ require ( github.com/rs/cors v1.11.1 // @grafana/identity-access-team github.com/russellhaering/goxmldsig v1.4.0 // @grafana/grafana-backend-group github.com/shopspring/decimal v1.4.0 // @grafana/grafana-datasources-core-services - github.com/spf13/cobra v1.9.1 // @grafana/grafana-app-platform-squad - github.com/spf13/pflag v1.0.7 // @grafana-app-platform-squad + github.com/spf13/cobra v1.10.1 // @grafana/grafana-app-platform-squad + github.com/spf13/pflag v1.0.10 // @grafana-app-platform-squad github.com/spyzhov/ajson v0.9.6 // @grafana/grafana-sharing-squad github.com/stretchr/testify v1.11.1 // @grafana/grafana-backend-group github.com/thomaspoignant/go-feature-flag v1.42.0 // @grafana/grafana-backend-group diff --git a/go.sum b/go.sum index aa9d392df3b..8447e63cc44 100644 --- a/go.sum +++ b/go.sum @@ -2222,8 +2222,8 @@ github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+L github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= -github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= -github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= +github.com/prometheus/client_golang v1.23.1 h1:w6gXMLQGgd0jXXlote9lRHMe0nG01EbnJT+C0EJru2Y= +github.com/prometheus/client_golang v1.23.1/go.mod h1:br8j//v2eg2K5Vvna5klK8Ku5pcU5r4ll73v6ik5dIQ= 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= @@ -2254,8 +2254,8 @@ github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGy github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= -github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= -github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/common v0.66.0 h1:K/rJPHrG3+AoQs50r2+0t7zMnMzek2Vbv31OFVsMeVY= +github.com/prometheus/common v0.66.0/go.mod h1:Ux6NtV1B4LatamKE63tJBntoxD++xmtI/lK0VtEplN4= github.com/prometheus/common/assets v0.2.0/go.mod h1:D17UVUE12bHbim7HzwUvtqm6gwBEaDQ0F+hIGbFbccI= github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4= github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI= @@ -2395,16 +2395,16 @@ github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155 github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= -github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= +github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= 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/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/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.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= diff --git a/go.work.sum b/go.work.sum index 3a6b155ab72..4eebbab7079 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1350,8 +1350,12 @@ github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQ github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7 h1:xoIK0ctDddBMnc74udxJYBqlo9Ylnsp1waqjLsnef20= github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M= github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.23.1 h1:w6gXMLQGgd0jXXlote9lRHMe0nG01EbnJT+C0EJru2Y= +github.com/prometheus/client_golang v1.23.1/go.mod h1:br8j//v2eg2K5Vvna5klK8Ku5pcU5r4ll73v6ik5dIQ= github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/common v0.64.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/common v0.66.0 h1:K/rJPHrG3+AoQs50r2+0t7zMnMzek2Vbv31OFVsMeVY= +github.com/prometheus/common v0.66.0/go.mod h1:Ux6NtV1B4LatamKE63tJBntoxD++xmtI/lK0VtEplN4= github.com/prometheus/common/assets v0.2.0 h1:0P5OrzoHrYBOSM1OigWL3mY8ZvV2N4zIE/5AahrSrfM= github.com/prometheus/statsd_exporter v0.26.1 h1:ucbIAdPmwAUcA+dU+Opok8Qt81Aw8HanlO+2N/Wjv7w= github.com/prometheus/statsd_exporter v0.26.1/go.mod h1:XlDdjAmRmx3JVvPPYuFNUg+Ynyb5kR69iPPkQjxXFMk= @@ -1411,7 +1415,12 @@ github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= +github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 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= github.com/substrait-io/substrait v0.66.1-0.20250205013839-a30b3e2d7ec6 h1:XqtxwYFCjS4L0o1QD4ipGHCuFG94U0f6BeldbilGQjU= diff --git a/pkg/aggregator/go.mod b/pkg/aggregator/go.mod index d891f959abc..1c73fe6d54d 100644 --- a/pkg/aggregator/go.mod +++ b/pkg/aggregator/go.mod @@ -63,6 +63,7 @@ require ( github.com/gorilla/mux v1.8.1 // indirect github.com/grafana/otel-profiling-go v0.5.1 // indirect 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 v1.4.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect @@ -100,15 +101,15 @@ require ( github.com/pierrec/lz4/v4 v4.1.22 // 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.23.0 // indirect + github.com/prometheus/client_golang v1.23.1 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.65.0 // indirect + github.com/prometheus/common v0.66.0 // indirect github.com/prometheus/procfs v0.16.1 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/smartystreets/goconvey v1.8.1 // indirect - github.com/spf13/cobra v1.9.1 // indirect - github.com/spf13/pflag v1.0.7 // indirect + github.com/spf13/cobra v1.10.1 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/ugorji/go/codec v1.2.11 // indirect @@ -158,6 +159,7 @@ require ( gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect diff --git a/pkg/aggregator/go.sum b/pkg/aggregator/go.sum index 733a9307fd4..5384797bc49 100644 --- a/pkg/aggregator/go.sum +++ b/pkg/aggregator/go.sum @@ -145,6 +145,8 @@ github.com/grafana/otel-profiling-go v0.5.1 h1:stVPKAFZSa7eGiqbYuG25VcqYksR6iWvF github.com/grafana/otel-profiling-go v0.5.1/go.mod h1:ftN/t5A/4gQI19/8MoWurBEtC6gFw8Dns1sJZ9W4Tls= github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg= github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o= @@ -257,16 +259,16 @@ 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.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= -github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= +github.com/prometheus/client_golang v1.23.1 h1:w6gXMLQGgd0jXXlote9lRHMe0nG01EbnJT+C0EJru2Y= +github.com/prometheus/client_golang v1.23.1/go.mod h1:br8j//v2eg2K5Vvna5klK8Ku5pcU5r4ll73v6ik5dIQ= 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= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= -github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/common v0.66.0 h1:K/rJPHrG3+AoQs50r2+0t7zMnMzek2Vbv31OFVsMeVY= +github.com/prometheus/common v0.66.0/go.mod h1:Ux6NtV1B4LatamKE63tJBntoxD++xmtI/lK0VtEplN4= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= @@ -290,11 +292,11 @@ github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sS github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= -github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= -github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= +github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/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= @@ -519,6 +521,7 @@ gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/apimachinery/go.mod b/pkg/apimachinery/go.mod index 2c89407b8c8..5e11c96d5c3 100644 --- a/pkg/apimachinery/go.mod +++ b/pkg/apimachinery/go.mod @@ -34,7 +34,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.14.1 // indirect - github.com/spf13/pflag v1.0.7 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect diff --git a/pkg/apimachinery/go.sum b/pkg/apimachinery/go.sum index 0803e9c7919..3518a187dae 100644 --- a/pkg/apimachinery/go.sum +++ b/pkg/apimachinery/go.sum @@ -63,8 +63,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.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= diff --git a/pkg/apiserver/go.mod b/pkg/apiserver/go.mod index e863b538af4..93b0d96d8a4 100644 --- a/pkg/apiserver/go.mod +++ b/pkg/apiserver/go.mod @@ -7,7 +7,7 @@ require ( github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43 github.com/grafana/grafana-app-sdk/logging v0.40.3 github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e - github.com/prometheus/client_golang v1.23.0 + github.com/prometheus/client_golang v1.23.1 github.com/stretchr/testify v1.11.1 go.opentelemetry.io/contrib/propagators/jaeger v1.36.0 go.opentelemetry.io/otel v1.38.0 @@ -45,6 +45,7 @@ require ( github.com/google/uuid v1.6.0 // indirect github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43 // indirect github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 // indirect + github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect @@ -62,9 +63,9 @@ require ( 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.2 // indirect - github.com/prometheus/common v0.65.0 // indirect + github.com/prometheus/common v0.66.0 // indirect github.com/prometheus/procfs v0.16.1 // indirect - github.com/spf13/pflag v1.0.7 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/x448/float16 v0.8.4 // indirect go.etcd.io/bbolt v1.4.0 // indirect @@ -98,6 +99,7 @@ require ( google.golang.org/protobuf v1.36.8 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/api v0.33.3 // indirect k8s.io/client-go v0.33.3 // indirect diff --git a/pkg/apiserver/go.sum b/pkg/apiserver/go.sum index fae4c4614b5..10213c03aac 100644 --- a/pkg/apiserver/go.sum +++ b/pkg/apiserver/go.sum @@ -88,6 +88,8 @@ github.com/grafana/grafana-app-sdk/logging v0.40.3 h1:2VXsXXEQiqAavRP8wusRDB6rDq github.com/grafana/grafana-app-sdk/logging v0.40.3/go.mod h1:otUD9XpJD7A5sCLb8mcs9hIXGdeV6lnhzVwe747g4RU= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e h1:BTKk7LHuG1kmAkucwTA7DuMbKpKvJTKrGdBmUNO4dfQ= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e/go.mod h1:IA4SOwun8QyST9c5UNs/fN37XL6boXXDvRYFcFwbipg= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 h1:uGoIog/wiQHI9GAxXO5TJbT0wWKH3O9HhOJW1F9c3fY= @@ -140,16 +142,16 @@ 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.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= -github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= +github.com/prometheus/client_golang v1.23.1 h1:w6gXMLQGgd0jXXlote9lRHMe0nG01EbnJT+C0EJru2Y= +github.com/prometheus/client_golang v1.23.1/go.mod h1:br8j//v2eg2K5Vvna5klK8Ku5pcU5r4ll73v6ik5dIQ= 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= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= -github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/common v0.66.0 h1:K/rJPHrG3+AoQs50r2+0t7zMnMzek2Vbv31OFVsMeVY= +github.com/prometheus/common v0.66.0/go.mod h1:Ux6NtV1B4LatamKE63tJBntoxD++xmtI/lK0VtEplN4= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= @@ -160,8 +162,8 @@ 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/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= @@ -363,6 +365,8 @@ gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/pkg/promlib/go.mod b/pkg/promlib/go.mod index ef4aa0d8670..6cd2f65a6fa 100644 --- a/pkg/promlib/go.mod +++ b/pkg/promlib/go.mod @@ -6,8 +6,8 @@ require ( github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 github.com/grafana/grafana-plugin-sdk-go v0.278.0 github.com/json-iterator/go v1.1.12 - github.com/prometheus/client_golang v1.23.0 - github.com/prometheus/common v0.65.0 + github.com/prometheus/client_golang v1.23.1 + github.com/prometheus/common v0.66.0 github.com/prometheus/prometheus v0.303.1 github.com/stretchr/testify v1.11.1 go.opentelemetry.io/otel v1.38.0 @@ -93,7 +93,7 @@ require ( github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/smartystreets/goconvey v1.8.1 // indirect - github.com/spf13/pflag v1.0.7 // indirect + github.com/spf13/pflag v1.0.10 // 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 @@ -132,6 +132,7 @@ require ( google.golang.org/grpc v1.75.0 // indirect gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/client-go v0.33.3 // indirect k8s.io/klog/v2 v2.130.1 // indirect diff --git a/pkg/promlib/go.sum b/pkg/promlib/go.sum index be9dfb735fc..27480578065 100644 --- a/pkg/promlib/go.sum +++ b/pkg/promlib/go.sum @@ -247,12 +247,12 @@ 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.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= -github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= +github.com/prometheus/client_golang v1.23.1 h1:w6gXMLQGgd0jXXlote9lRHMe0nG01EbnJT+C0EJru2Y= +github.com/prometheus/client_golang v1.23.1/go.mod h1:br8j//v2eg2K5Vvna5klK8Ku5pcU5r4ll73v6ik5dIQ= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= -github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/common v0.66.0 h1:K/rJPHrG3+AoQs50r2+0t7zMnMzek2Vbv31OFVsMeVY= +github.com/prometheus/common v0.66.0/go.mod h1:Ux6NtV1B4LatamKE63tJBntoxD++xmtI/lK0VtEplN4= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/prometheus/prometheus v0.303.1 h1:He/2jRE6sB23Ew38AIoR1WRR3fCMgPlJA2E0obD2WSY= @@ -274,8 +274,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.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY= github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= -github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= diff --git a/pkg/services/ngalert/api/validation/api_ruler_validation.go b/pkg/services/ngalert/api/validation/api_ruler_validation.go index 07f3099dbb1..c2baf8108ba 100644 --- a/pkg/services/ngalert/api/validation/api_ruler_validation.go +++ b/pkg/services/ngalert/api/validation/api_ruler_validation.go @@ -182,7 +182,7 @@ func validateRecordingRuleFields(in *apimodels.PostableExtendedRuleNode, newRule if !metricName.IsValid() { return ngmodels.AlertRule{}, fmt.Errorf("%w: %s", ngmodels.ErrAlertRuleFailedValidation, "metric name for recording rule must be a valid utf8 string") } - if !prommodels.IsValidMetricName(metricName) { + if !prommodels.IsValidMetricName(metricName) { // nolint:staticcheck return ngmodels.AlertRule{}, fmt.Errorf("%w: %s", ngmodels.ErrAlertRuleFailedValidation, "metric name for recording rule must be a valid Prometheus metric name") } newRule.Record = ModelRecordFromApiRecord(in.GrafanaManagedAlert.Record) diff --git a/pkg/services/ngalert/models/alert_rule.go b/pkg/services/ngalert/models/alert_rule.go index 9b9d211dd3b..c7350a91620 100644 --- a/pkg/services/ngalert/models/alert_rule.go +++ b/pkg/services/ngalert/models/alert_rule.go @@ -18,9 +18,10 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - "github.com/grafana/grafana-plugin-sdk-go/data" prommodels "github.com/prometheus/common/model" + "github.com/grafana/grafana-plugin-sdk-go/data" + alertingModels "github.com/grafana/alerting/models" "github.com/grafana/grafana/pkg/services/folder" @@ -783,7 +784,7 @@ func validateRecordingRuleFields(rule *AlertRule) error { if !metricName.IsValid() { return errors.New("metric name for recording rule must be a valid utf8 string") } - if !prommodels.IsValidMetricName(metricName) { + if !prommodels.IsValidMetricName(metricName) { // nolint:staticcheck return errors.New("metric name for recording rule must be a valid Prometheus metric name") } diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index 4854e821857..55b5d7da60d 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -767,7 +767,7 @@ func (cfg *Cfg) readGrafanaEnvironmentMetrics() error { labelName := model.LabelName(key.Name()) labelValue := model.LabelValue(key.Value()) - if !labelName.IsValid() { + if !labelName.IsValid() { // nolint:staticcheck return fmt.Errorf("invalid label name in [metrics.environment_info] configuration. name %q", labelName) } From ba65aa6529817f1cae2459769ad90b7999fe79e3 Mon Sep 17 00:00:00 2001 From: Jo Date: Mon, 15 Sep 2025 11:47:08 +0200 Subject: [PATCH 20/33] AccessControl: Remove deprecated scope split migration (#111071) remove scope migrator --- pkg/services/accesscontrol/acimpl/service.go | 8 -- .../accesscontrol/migrator/migrator.go | 95 ------------------- .../migrator/migrator_bench_test.go | 27 ------ .../accesscontrol/migrator/migrator_test.go | 68 ------------- 4 files changed, 198 deletions(-) delete mode 100644 pkg/services/accesscontrol/migrator/migrator_bench_test.go diff --git a/pkg/services/accesscontrol/acimpl/service.go b/pkg/services/accesscontrol/acimpl/service.go index ecc0210edcd..6afca8dd946 100644 --- a/pkg/services/accesscontrol/acimpl/service.go +++ b/pkg/services/accesscontrol/acimpl/service.go @@ -73,14 +73,6 @@ func ProvideService( return nil, err } - // Migrating scopes that haven't been split yet to have kind, attribute and identifier in the DB - // This will be removed once we've: - // 1) removed the feature toggle and - // 2) have released enough versions not to support a version without split scopes - if err := migrator.MigrateScopeSplit(db, service.log); err != nil { - return nil, err - } - // Migrating to remove deprecated permissions from the database if err := migrator.MigrateRemoveDeprecatedPermissions(db, service.log); err != nil { return nil, err diff --git a/pkg/services/accesscontrol/migrator/migrator.go b/pkg/services/accesscontrol/migrator/migrator.go index 1463ffd9eaa..5e19c91b3e9 100644 --- a/pkg/services/accesscontrol/migrator/migrator.go +++ b/pkg/services/accesscontrol/migrator/migrator.go @@ -18,94 +18,6 @@ var ( batchSize = 1000 ) -const ( - maxLen = 40 -) - -func MigrateScopeSplit(db db.DB, log log.Logger) error { - t := time.Now() - ctx := context.Background() - cnt := 0 - - // Search for the permissions to update - var permissions []ac.Permission - if errFind := db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error { - return sess.SQL("SELECT * FROM permission WHERE NOT scope = '' AND identifier = ''").Find(&permissions) - }); errFind != nil { - log.Error("Could not search for permissions to update", "migration", "scopeSplit", "error", errFind) - return errFind - } - - if len(permissions) == 0 { - log.Debug("No permission require a scope split", "migration", "scopeSplit") - return nil - } - - errBatchUpdate := batch(len(permissions), batchSize, func(start, end int) error { - n := end - start - - // IDs to remove - delQuery := "DELETE FROM permission WHERE id IN (" - delArgs := make([]any, 0, n) - - // Query to insert the updated permissions - insertQuery := "INSERT INTO permission (id, role_id, action, scope, kind, attribute, identifier, created, updated) VALUES " - insertArgs := make([]any, 0, 9*n) - - // Prepare batch of updated permissions - for i := start; i < end; i++ { - kind, attribute, identifier := permissions[i].SplitScope() - - // Trim to max length to avoid bootloop. - // too long scopes will be truncated and the permission will become invalid. - kind = trimToMaxLen(kind, maxLen) - attribute = trimToMaxLen(attribute, maxLen) - identifier = trimToMaxLen(identifier, maxLen) - - delQuery += "?," - delArgs = append(delArgs, permissions[i].ID) - - insertQuery += "(?, ?, ?, ?, ?, ?, ?, ?, ?)," - insertArgs = append(insertArgs, permissions[i].ID, permissions[i].RoleID, - permissions[i].Action, permissions[i].Scope, - kind, attribute, identifier, - permissions[i].Created, t, - ) - } - // Remove trailing ',' - insertQuery = insertQuery[:len(insertQuery)-1] - - // Remove trailing ',' and close brackets - delQuery = delQuery[:len(delQuery)-1] + ")" - - // Batch update the permissions - if errBatchUpdate := db.GetSqlxSession().WithTransaction(ctx, func(tx *session.SessionTx) error { - if _, errDel := tx.Exec(ctx, delQuery, delArgs...); errDel != nil { - log.Error("Error deleting permissions", "migration", "scopeSplit", "error", errDel) - return errDel - } - if _, errInsert := tx.Exec(ctx, insertQuery, insertArgs...); errInsert != nil { - log.Error("Error saving permissions", "migration", "scopeSplit", "error", errInsert) - return errInsert - } - return nil - }); errBatchUpdate != nil { - log.Error("Error updating permission batch", "migration", "scopeSplit", "start", start, "end", end) - return errBatchUpdate - } - - cnt += end - start - return nil - }) - if errBatchUpdate != nil { - log.Error("Could not migrate permissions", "migration", "scopeSplit", "total", len(permissions), "succeeded", cnt, "left", len(permissions)-cnt, "error", errBatchUpdate) - return errBatchUpdate - } - - log.Debug("Migrated permissions", "migration", "scopeSplit", "total", len(permissions), "succeeded", cnt, "in", time.Since(t)) - return nil -} - func batch(count, batchSize int, eachFn func(start, end int) error) error { for i := 0; i < count; { end := i + batchSize @@ -206,10 +118,3 @@ func MigrateRemoveDeprecatedPermissions(db db.DB, log log.Logger) error { log.Info("Completed migration to remove deprecated permissions", "migration", "removeDeprecatedPermissions", "totalRemoved", totalRemoved, "duration", time.Since(t)) return nil } - -func trimToMaxLen(s string, maxLen int) string { - if len(s) > maxLen { - return s[:maxLen] - } - return s -} diff --git a/pkg/services/accesscontrol/migrator/migrator_bench_test.go b/pkg/services/accesscontrol/migrator/migrator_bench_test.go deleted file mode 100644 index 5257a2fb2dd..00000000000 --- a/pkg/services/accesscontrol/migrator/migrator_bench_test.go +++ /dev/null @@ -1,27 +0,0 @@ -package migrator - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/grafana/grafana/pkg/infra/db" - "github.com/grafana/grafana/pkg/infra/log" -) - -func benchScopeSplitConcurrent(b *testing.B, count int) { - store := db.InitTestDB(b) - // Populate permissions - require.NoError(b, batchInsertPermissions(count, store), "could not insert permissions") - logger := log.New("migrator.test") - b.ResetTimer() - - for n := 0; n < b.N; n++ { - err := MigrateScopeSplit(store, logger) - require.NoError(b, err) - } -} - -func BenchmarkMigrateScopeSplitConcurrent_50K(b *testing.B) { benchScopeSplitConcurrent(b, 50000) } - -func BenchmarkMigrateScopeSplitConcurrent_100K(b *testing.B) { benchScopeSplitConcurrent(b, 100000) } diff --git a/pkg/services/accesscontrol/migrator/migrator_test.go b/pkg/services/accesscontrol/migrator/migrator_test.go index 5595a251c81..0f2b55d1528 100644 --- a/pkg/services/accesscontrol/migrator/migrator_test.go +++ b/pkg/services/accesscontrol/migrator/migrator_test.go @@ -13,7 +13,6 @@ import ( "github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/log" ac "github.com/grafana/grafana/pkg/services/accesscontrol" - "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/tests/testsuite" "github.com/grafana/grafana/pkg/util/testutil" ) @@ -22,73 +21,6 @@ func TestMain(m *testing.M) { testsuite.Run(m) } -func batchInsertPermissions(cnt int, sqlStore db.DB) error { - now := time.Now() - - return batch(cnt, batchSize, func(start, end int) error { - n := end - start - permissions := make([]ac.Permission, 0, n) - for i := start + 1; i < end+1; i++ { - permissions = append(permissions, ac.Permission{ - RoleID: 1, - Action: "action", - Scope: fmt.Sprintf("resource:uid:%v", i), - Created: now, - Updated: now, - }) - } - return sqlStore.WithDbSession(context.Background(), func(sess *db.Session) error { - _, err := sess.Insert(permissions) - return err - }) - }) -} - -// TestIntegrationMigrateScopeSplit tests the scope split migration -// also tests the scope split truncation logic -func TestIntegrationMigrateScopeSplitTruncation(t *testing.T) { - testutil.SkipIntegrationTestInShortMode(t) - - sqlStore := db.InitTestDB(t) - logger := log.New("accesscontrol.migrator.test") - - batchSize = 20 - // Populate permissions - require.NoError(t, batchInsertPermissions(3*batchSize, sqlStore), "could not insert permissions") - - // Insert a permission with a scope longer than 240 characters - longScope := strings.Repeat("a", 60) + ":" + strings.Repeat("b", 60) + ":" + strings.Repeat("c", 60) - permission := ac.Permission{ - RoleID: 1, - Action: "action", - Scope: longScope, - Created: time.Now(), - Updated: time.Now(), - } - require.NoError(t, sqlStore.WithDbSession(context.Background(), func(sess *db.Session) error { - _, err := sess.Insert(permission) - return err - }), "could not insert permission with long scope") - - // Migrate - require.NoError(t, MigrateScopeSplit(sqlStore, logger)) - - // Check migration result - permissions := make([]ac.Permission, 0, 3*batchSize+1) - errFind := sqlStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error { - return sess.Find(&permissions) - }) - require.NoError(t, errFind, "could not find permissions in store") - - for i := range permissions { - if permissions[i].Scope == longScope { - assert.Equal(t, strings.Repeat("a", 40), permissions[i].Kind) - assert.Equal(t, strings.Repeat("b", 40), permissions[i].Attribute) - assert.Equal(t, strings.Repeat("c", 40), permissions[i].Identifier) - } - } -} - // batchInsertTestPermissions inserts test permissions for migration testing func batchInsertTestPermissions(cnt int, sqlStore db.DB, actionPrefix string) error { now := time.Now() From a52303de8d651b32178c65906250717aee46986b Mon Sep 17 00:00:00 2001 From: Costa Alexoglou Date: Mon, 15 Sep 2025 11:52:01 +0200 Subject: [PATCH 21/33] chore: faster image building for mt-tilt (#111074) --- .dockerignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.dockerignore b/.dockerignore index 1df915d2461..191b8881ded 100644 --- a/.dockerignore +++ b/.dockerignore @@ -4,6 +4,7 @@ .gitignore .vscode bin +!bin/grafana-linux-k8s data* dist docker From e5fb888e6ffe830e8c0f36053da6266b52e956ef Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 15 Sep 2025 10:57:47 +0100 Subject: [PATCH 22/33] Update dependency @grafana/google-sdk to v0.3.5 (#110756) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- .../datasource/cloud-monitoring/package.json | 2 +- yarn.lock | 28 +++++-------------- 3 files changed, 9 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index 747a6a6bce3..22095887212 100644 --- a/package.json +++ b/package.json @@ -277,7 +277,7 @@ "@grafana/faro-web-sdk": "^1.19.0", "@grafana/faro-web-tracing": "^1.19.0", "@grafana/flamegraph": "workspace:*", - "@grafana/google-sdk": "0.3.4", + "@grafana/google-sdk": "0.3.5", "@grafana/i18n": "workspace:*", "@grafana/lezer-logql": "0.2.8", "@grafana/llm": "0.22.1", diff --git a/public/app/plugins/datasource/cloud-monitoring/package.json b/public/app/plugins/datasource/cloud-monitoring/package.json index 34eab1a19fb..a8a2c90785a 100644 --- a/public/app/plugins/datasource/cloud-monitoring/package.json +++ b/public/app/plugins/datasource/cloud-monitoring/package.json @@ -6,7 +6,7 @@ "dependencies": { "@emotion/css": "11.13.5", "@grafana/data": "12.3.0-pre", - "@grafana/google-sdk": "0.3.4", + "@grafana/google-sdk": "0.3.5", "@grafana/plugin-ui": "^0.10.10", "@grafana/runtime": "12.3.0-pre", "@grafana/schema": "12.3.0-pre", diff --git a/yarn.lock b/yarn.lock index 91c46d31c3c..074991cd666 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2817,7 +2817,7 @@ __metadata: "@emotion/css": "npm:11.13.5" "@grafana/data": "npm:12.3.0-pre" "@grafana/e2e-selectors": "npm:12.3.0-pre" - "@grafana/google-sdk": "npm:0.3.4" + "@grafana/google-sdk": "npm:0.3.5" "@grafana/plugin-configs": "npm:12.3.0-pre" "@grafana/plugin-ui": "npm:^0.10.10" "@grafana/runtime": "npm:12.3.0-pre" @@ -3227,15 +3227,15 @@ __metadata: languageName: unknown linkType: soft -"@grafana/google-sdk@npm:0.3.4": - version: 0.3.4 - resolution: "@grafana/google-sdk@npm:0.3.4" +"@grafana/google-sdk@npm:0.3.5": + version: 0.3.5 + resolution: "@grafana/google-sdk@npm:0.3.5" peerDependencies: "@grafana/data": ">=10.4.0" "@grafana/ui": ">=10.4.0" react: ^18.2.0 react-dom: ^18.2.0 - checksum: 10/6156a07ab9a8e31ac20a689448f7d6bc86bd9d3fd0b22de8182cf5e6f96c05054f8c53a38148de58dca807adb5b0563914b03a22308ec7f770bf1cc71e8f21bd + checksum: 10/5070002bc4e4ca4079384e914a0435d6deffe0854b3c2a8ce8a691cdcfa81d9f8ba9247222fbd366f48a8bb0745f8ba2c21790e7f4a46f027d0a867f8c75e06e languageName: node linkType: hard @@ -18158,7 +18158,7 @@ __metadata: "@grafana/faro-web-sdk": "npm:^1.19.0" "@grafana/faro-web-tracing": "npm:^1.19.0" "@grafana/flamegraph": "workspace:*" - "@grafana/google-sdk": "npm:0.3.4" + "@grafana/google-sdk": "npm:0.3.5" "@grafana/i18n": "workspace:*" "@grafana/lezer-logql": "npm:0.2.8" "@grafana/llm": "npm:0.22.1" @@ -19258,21 +19258,7 @@ __metadata: languageName: node linkType: hard -"i18next@npm:^25.0.0": - version: 25.3.2 - resolution: "i18next@npm:25.3.2" - dependencies: - "@babel/runtime": "npm:^7.27.6" - peerDependencies: - typescript: ^5 - peerDependenciesMeta: - typescript: - optional: true - checksum: 10/fb6b2035cc8f3bcc89f56e164d22cefbefd54e5a569315b20ddcfa6e1b68c48962307181b271fea7e5ee37ddfaa90721a4a7f7814b739f24bf00a3a670b8eb93 - languageName: node - linkType: hard - -"i18next@npm:^25.5.2": +"i18next@npm:^25.0.0, i18next@npm:^25.5.2": version: 25.5.2 resolution: "i18next@npm:25.5.2" dependencies: From f392bb6f94e749bfa5c70d297e4952d34cf3d903 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 15 Sep 2025 10:24:48 +0000 Subject: [PATCH 23/33] Update dependency @grafana/lezer-traceql to v0.0.24 (#111078) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- public/app/plugins/datasource/tempo/package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/public/app/plugins/datasource/tempo/package.json b/public/app/plugins/datasource/tempo/package.json index fac7933eb79..ad5f17e3c35 100644 --- a/public/app/plugins/datasource/tempo/package.json +++ b/public/app/plugins/datasource/tempo/package.json @@ -7,7 +7,7 @@ "@emotion/css": "11.13.5", "@grafana/data": "workspace:*", "@grafana/e2e-selectors": "workspace:*", - "@grafana/lezer-traceql": "0.0.23", + "@grafana/lezer-traceql": "0.0.24", "@grafana/monaco-logql": "^0.0.8", "@grafana/o11y-ds-frontend": "workspace:*", "@grafana/plugin-ui": "^0.10.10", diff --git a/yarn.lock b/yarn.lock index 074991cd666..55a99589c60 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2864,7 +2864,7 @@ __metadata: "@emotion/css": "npm:11.13.5" "@grafana/data": "workspace:*" "@grafana/e2e-selectors": "workspace:*" - "@grafana/lezer-traceql": "npm:0.0.23" + "@grafana/lezer-traceql": "npm:0.0.24" "@grafana/monaco-logql": "npm:^0.0.8" "@grafana/o11y-ds-frontend": "workspace:*" "@grafana/plugin-configs": "npm:12.3.0-pre" @@ -3269,12 +3269,12 @@ __metadata: languageName: node linkType: hard -"@grafana/lezer-traceql@npm:0.0.23": - version: 0.0.23 - resolution: "@grafana/lezer-traceql@npm:0.0.23" +"@grafana/lezer-traceql@npm:0.0.24": + version: 0.0.24 + resolution: "@grafana/lezer-traceql@npm:0.0.24" peerDependencies: "@lezer/lr": ^1.4.2 - checksum: 10/e9dba8ed747062c791fd2298e20e18da06642d273f9c502f8c1ec90524108edf7c6bd4e719db2ae62de77ba02bfaf0d2f291058d04727969b5af67cdb277b9c3 + checksum: 10/46bd65d0b61f490dc92db318062079c50564429407e7496def9224b061231d080943b20e63c066ea12ab0929374a50cfa1d29fb2156a65b3ef635f44011bd959 languageName: node linkType: hard From df2bb6be0a104528e0c93c219fe057f7efcb56dc Mon Sep 17 00:00:00 2001 From: Andreas Christou Date: Mon, 15 Sep 2025 12:35:29 +0200 Subject: [PATCH 24/33] Graphite: Backend tag values autocomplete endpoint (#110773) * Add lint rules * Backend decoupling - Add standalone files - Add graphite query type - Add logger to Service - Create logger in the ProvideService method - Use a pointer for the HTTP client provider - Update logger usage everywhere - Update tracer type - Replace simplejson with json - Add dummy CallResource and CheckHealth methods - Update tests * Update ConfigEditor imports * Update types imports * Update datasource - Switch to using semver package - Update imports * Update store imports * Update helper imports and notification creation * Update context import * Update version numbers and logic * Copy array_move from core * Test updates * Add required files and update plugin.json * Update core references and packages * Remove commented code * Update wire * Lint * Fix import * Copy null type * More lint * Update snapshot * Refactor backend - Split query logic into separate file - Move utils to separate file * Add health-check logic - Support backend healthcheck if the FF is enabled * Remove query import support as unneeded * Add test * Add util function for decoding responses * Add events types * Add resource handler * Add events handler and generic resource req handler * Tests * Update frontend - Add types - Update events function to support backend requests * Lint and typing * Lint * Add metrics find endpoint - Add types - Add generic response parser - Add endpoint - Tests * Update FE functoin to use backend endpoint * Lint * Simplify request * Update test * Metrics expand type * Extract shared logic and add metric expand endpoint * Update tests * Call metric expand from backend * Rename type for clarity * Add get resource req handler * Refactor doGraphiteRequest, parseResponse Update tests * Migrate functions endpoint to backend * Support tags autocomplete in backend - Add tests - Add types - Remove unneeded comments * Support tag values autocomplete - Remove unused frontend endpoints - Add types - Update tests * Add tests * Review * Review * Fix packages * Format * Fix merge issues * Review * Fix undefined values * Extract request creation - Add method for create requests generically with tests - Replace usage in query method - Update usages in resource handlers - Update tests - Update types * Lint --- eslint-suppressions.json | 2 +- pkg/tsdb/graphite/graphite.go | 6 +- pkg/tsdb/graphite/graphite_test.go | 62 +++++---- pkg/tsdb/graphite/resource_handler.go | 68 +++++++--- pkg/tsdb/graphite/resource_handler_test.go | 122 ++++++++++++++++++ pkg/tsdb/graphite/types.go | 11 +- .../plugins/datasource/graphite/datasource.ts | 81 +++--------- 7 files changed, 243 insertions(+), 109 deletions(-) diff --git a/eslint-suppressions.json b/eslint-suppressions.json index 8d4775e9a30..91901fdad41 100644 --- a/eslint-suppressions.json +++ b/eslint-suppressions.json @@ -4108,7 +4108,7 @@ "count": 4 }, "@typescript-eslint/no-explicit-any": { - "count": 10 + "count": 8 } }, "public/app/plugins/datasource/graphite/gfunc.ts": { diff --git a/pkg/tsdb/graphite/graphite.go b/pkg/tsdb/graphite/graphite.go index 62b932763af..512598da1b7 100644 --- a/pkg/tsdb/graphite/graphite.go +++ b/pkg/tsdb/graphite/graphite.go @@ -109,8 +109,10 @@ func (s *Service) createRequest(ctx context.Context, dsInfo *datasourceInfo, par if params.QueryParams != nil { queryValues := u.Query() - for k, v := range params.QueryParams { - queryValues.Set(k, v) + for key, values := range params.QueryParams { + for _, value := range values { + queryValues.Add(key, value) + } } u.RawQuery = queryValues.Encode() } diff --git a/pkg/tsdb/graphite/graphite_test.go b/pkg/tsdb/graphite/graphite_test.go index bff49068f30..9234c6bf4a3 100644 --- a/pkg/tsdb/graphite/graphite_test.go +++ b/pkg/tsdb/graphite/graphite_test.go @@ -26,7 +26,7 @@ func Test_CreateRequest(t *testing.T) { expectedMethod string expectedError string checkHeaders map[string]string - checkQuery map[string]string + checkQuery map[string][]string }{ { name: "basic request with default GET method", @@ -57,16 +57,16 @@ func Test_CreateRequest(t *testing.T) { name: "request with query parameters", dsInfo: dsInfo, params: URLParams{ - QueryParams: map[string]string{ - "query": "stats.counters.*", - "format": "json", + QueryParams: map[string][]string{ + "query": {"stats.counters.*"}, + "format": {"json"}, }, }, expectedURL: "http://graphite.example.com", expectedMethod: "GET", - checkQuery: map[string]string{ - "query": "stats.counters.*", - "format": "json", + checkQuery: map[string][]string{ + "query": {"stats.counters.*"}, + "format": {"json"}, }, }, { @@ -99,9 +99,9 @@ func Test_CreateRequest(t *testing.T) { params: URLParams{ SubPath: "/metrics/expand", Method: "POST", - QueryParams: map[string]string{ - "groupByExpr": "true", - "leavesOnly": "false", + QueryParams: map[string][]string{ + "groupByExpr": {"true"}, + "leavesOnly": {"false"}, }, Headers: map[string]string{ "X-Custom-Header": "test-value", @@ -110,9 +110,9 @@ func Test_CreateRequest(t *testing.T) { }, expectedURL: "http://graphite.example.com/metrics/expand", expectedMethod: "POST", - checkQuery: map[string]string{ - "groupByExpr": "true", - "leavesOnly": "false", + checkQuery: map[string][]string{ + "groupByExpr": {"true"}, + "leavesOnly": {"false"}, }, checkHeaders: map[string]string{ "X-Custom-Header": "test-value", @@ -130,16 +130,30 @@ func Test_CreateRequest(t *testing.T) { name: "empty query parameter values", dsInfo: dsInfo, params: URLParams{ - QueryParams: map[string]string{ - "empty": "", - "valid": "value", + QueryParams: map[string][]string{ + "empty": {""}, + "valid": {"value"}, }, }, expectedURL: "http://graphite.example.com", expectedMethod: "GET", - checkQuery: map[string]string{ - "empty": "", - "valid": "value", + checkQuery: map[string][]string{ + "empty": {""}, + "valid": {"value"}, + }, + }, + { + name: "multi-valued query parameter", + dsInfo: dsInfo, + params: URLParams{ + QueryParams: map[string][]string{ + "valid": {"value1", "value2"}, + }, + }, + expectedURL: "http://graphite.example.com", + expectedMethod: "GET", + checkQuery: map[string][]string{ + "valid": {"value1", "value2"}, }, }, } @@ -163,9 +177,13 @@ func Test_CreateRequest(t *testing.T) { assert.Equal(t, tt.expectedMethod, req.Method) if tt.checkQuery != nil { - for key, expectedValue := range tt.checkQuery { - actualValue := req.URL.Query().Get(key) - assert.Equal(t, expectedValue, actualValue, "Query parameter %s", key) + for key, expectedValues := range tt.checkQuery { + actualValue := req.URL.Query()[key] + assert.NotZero(t, len(actualValue)) + + for _, expectedValue := range expectedValues { + assert.Contains(t, actualValue, expectedValue, "Query parameter %s", key) + } } } diff --git a/pkg/tsdb/graphite/resource_handler.go b/pkg/tsdb/graphite/resource_handler.go index d8a1c3508ec..dfd1a3c6568 100644 --- a/pkg/tsdb/graphite/resource_handler.go +++ b/pkg/tsdb/graphite/resource_handler.go @@ -26,6 +26,8 @@ func (s *Service) newResourceMux() *http.ServeMux { mux.HandleFunc("/metrics/expand", handleResourceReq(s.handleMetricsExpand, s)) mux.HandleFunc("/functions", handleResourceReq(s.handleFunctions, s)) mux.HandleFunc("/tags/autoComplete/tags", handleResourceReq(s.handleTagsAutocomplete, s)) + mux.HandleFunc("/tags/autoComplete/values", handleResourceReq(s.handleTagValuesAutocomplete, s)) + return mux } @@ -87,12 +89,12 @@ func handleResourceReq[T any](handlerFn resourceHandler[T], s *Service) func(rw } func (s *Service) handleEvents(ctx context.Context, dsInfo *datasourceInfo, eventsRequestJson *GraphiteEventsRequest) ([]byte, int, error) { - queryParams := map[string]string{ - "from": eventsRequestJson.From, - "until": eventsRequestJson.Until, + queryParams := map[string][]string{ + "from": {eventsRequestJson.From}, + "until": {eventsRequestJson.Until}, } if eventsRequestJson.Tags != "" { - queryParams["tags"] = eventsRequestJson.Tags + queryParams["tags"] = []string{eventsRequestJson.Tags} } req, err := s.createRequest(ctx, dsInfo, URLParams{ @@ -128,12 +130,12 @@ func (s *Service) handleMetricsFind(ctx context.Context, dsInfo *datasourceInfo, data := url.Values{} data.Set("query", metricsFindRequestJson.Query) - queryParams := map[string]string{} + queryParams := map[string][]string{} if metricsFindRequestJson.From != "" { - queryParams["from"] = metricsFindRequestJson.From + queryParams["from"] = []string{metricsFindRequestJson.From} } if metricsFindRequestJson.Until != "" { - queryParams["until"] = metricsFindRequestJson.Until + queryParams["until"] = []string{metricsFindRequestJson.Until} } req, err := s.createRequest(ctx, dsInfo, URLParams{ @@ -165,14 +167,14 @@ func (s *Service) handleMetricsExpand(ctx context.Context, dsInfo *datasourceInf return nil, http.StatusBadRequest, fmt.Errorf("query is required") } - queryParams := map[string]string{ - "query": metricsExpandRequestJson.Query, + queryParams := map[string][]string{ + "query": {metricsExpandRequestJson.Query}, } if metricsExpandRequestJson.From != "" { - queryParams["from"] = metricsExpandRequestJson.From + queryParams["from"] = []string{metricsExpandRequestJson.From} } if metricsExpandRequestJson.Until != "" { - queryParams["until"] = metricsExpandRequestJson.Until + queryParams["until"] = []string{metricsExpandRequestJson.Until} } req, err := s.createRequest(ctx, dsInfo, URLParams{ @@ -205,11 +207,11 @@ func (s *Service) handleMetricsExpand(ctx context.Context, dsInfo *datasourceInf } func (s *Service) handleTagsAutocomplete(ctx context.Context, dsInfo *datasourceInfo, tagsAutocompleteRequestJson *GraphiteTagsRequest) ([]byte, int, error) { - queryParams := map[string]string{ - "from": tagsAutocompleteRequestJson.From, - "until": tagsAutocompleteRequestJson.Until, - "limit": fmt.Sprintf("%d", tagsAutocompleteRequestJson.Limit), - "tagPrefix": tagsAutocompleteRequestJson.TagPrefix, + queryParams := map[string][]string{ + "from": {tagsAutocompleteRequestJson.From}, + "until": {tagsAutocompleteRequestJson.Until}, + "limit": {fmt.Sprintf("%d", tagsAutocompleteRequestJson.Limit)}, + "tagPrefix": {tagsAutocompleteRequestJson.TagPrefix}, } req, err := s.createRequest(ctx, dsInfo, URLParams{ SubPath: "tags/autoComplete/tags", @@ -217,7 +219,7 @@ func (s *Service) handleTagsAutocomplete(ctx context.Context, dsInfo *datasource QueryParams: queryParams, }) if err != nil { - return nil, http.StatusInternalServerError, fmt.Errorf("failed to create metrics expand request %v", err) + return nil, http.StatusInternalServerError, fmt.Errorf("failed to create tags autocomplete request %v", err) } tags, _, statusCode, err := doGraphiteRequest[[]string](ctx, dsInfo, s.logger, req, false) @@ -233,6 +235,38 @@ func (s *Service) handleTagsAutocomplete(ctx context.Context, dsInfo *datasource return tagsResponse, statusCode, nil } +func (s *Service) handleTagValuesAutocomplete(ctx context.Context, dsInfo *datasourceInfo, tagValuesAutocompleteRequestJson *GraphiteTagValuesRequest) ([]byte, int, error) { + queryParams := map[string][]string{ + "expr": tagValuesAutocompleteRequestJson.Expr, + "tag": {tagValuesAutocompleteRequestJson.Tag}, + "from": {tagValuesAutocompleteRequestJson.From}, + "until": {tagValuesAutocompleteRequestJson.Until}, + "limit": {fmt.Sprintf("%d", tagValuesAutocompleteRequestJson.Limit)}, + "valuePrefix": {tagValuesAutocompleteRequestJson.ValuePrefix}, + } + + req, err := s.createRequest(ctx, dsInfo, URLParams{ + SubPath: "tags/autoComplete/values", + Method: http.MethodGet, + QueryParams: queryParams, + }) + if err != nil { + return nil, http.StatusInternalServerError, fmt.Errorf("failed to create tag values autocomplete request %v", err) + } + + tagValues, _, statusCode, err := doGraphiteRequest[[]string](ctx, dsInfo, s.logger, req, false) + if err != nil { + return nil, statusCode, fmt.Errorf("tag values autocomplete request failed: %v", err) + } + + tagValuesResponse, err := json.Marshal(tagValues) + if err != nil { + return nil, http.StatusInternalServerError, fmt.Errorf("failed to marshal tag values autocomplete response: %s", err) + } + + return tagValuesResponse, statusCode, nil +} + func (s *Service) handleFunctions(ctx context.Context, dsInfo *datasourceInfo, _ *any) ([]byte, int, error) { req, err := s.createRequest(ctx, dsInfo, URLParams{ SubPath: "functions", diff --git a/pkg/tsdb/graphite/resource_handler_test.go b/pkg/tsdb/graphite/resource_handler_test.go index c331c2d2cab..c4ba2e201d2 100644 --- a/pkg/tsdb/graphite/resource_handler_test.go +++ b/pkg/tsdb/graphite/resource_handler_test.go @@ -9,6 +9,7 @@ import ( "io" "net/http" "net/http/httptest" + "net/url" "testing" "github.com/grafana/grafana-plugin-sdk-go/backend" @@ -498,6 +499,127 @@ func TestHandleTagsAutocomplete(t *testing.T) { }) } } + +func TestHandleTagValuesAutocomplete(t *testing.T) { + tests := []struct { + name string + request GraphiteTagValuesRequest + responseBody string + statusCode int + expectError bool + errorContains string + expectedData []string + }{ + { + name: "successful tag values autocomplete request", + request: GraphiteTagValuesRequest{ + Expr: []string{"app=*"}, + Tag: "environment", + From: "1h", + Until: "now", + Limit: 5, + ValuePrefix: "prod", + }, + responseBody: `["production", "prod-eu", "prod-us"]`, + statusCode: 200, + expectedData: []string{"production", "prod-eu", "prod-us"}, + }, + { + name: "multiple expressions", + request: GraphiteTagValuesRequest{ + Expr: []string{"app=*", "region=us-*"}, + Tag: "environment", + From: "1h", + Until: "now", + Limit: 5, + ValuePrefix: "prod", + }, + responseBody: `["production", "prod-eu", "prod-us"]`, + statusCode: 200, + expectedData: []string{"production", "prod-eu", "prod-us"}, + }, + { + name: "tag values autocomplete with empty response", + request: GraphiteTagValuesRequest{ + Expr: []string{"app=nonexistent"}, + Tag: "environment", + ValuePrefix: "staging", + }, + responseBody: `[]`, + statusCode: 200, + expectedData: []string{}, + }, + { + name: "tag values autocomplete server error", + request: GraphiteTagValuesRequest{ + Expr: []string{"invalid-expr"}, + Tag: "env", + }, + responseBody: `invalid json response`, + statusCode: 400, + expectError: true, + errorContains: "tag values autocomplete request failed", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockTransport := &mockRoundTripper{ + respBody: []byte(tt.responseBody), + status: tt.statusCode, + } + + dsInfo := &datasourceInfo{ + HTTPClient: &http.Client{Transport: mockTransport}, + URL: "http://graphite.example.com", + } + + service := &Service{ + logger: log.NewNullLogger(), + } + + result, statusCode, err := service.handleTagValuesAutocomplete(context.Background(), dsInfo, &tt.request) + + if tt.expectError { + assert.Error(t, err) + if tt.errorContains != "" { + assert.Contains(t, err.Error(), tt.errorContains) + } + } else { + assert.NoError(t, err) + assert.Equal(t, tt.statusCode, statusCode) + + var tagValues []string + err = json.Unmarshal(result, &tagValues) + assert.NoError(t, err) + assert.Equal(t, tt.expectedData, tagValues) + } + + if !tt.expectError { + expectedURL := "http://graphite.example.com/tags/autoComplete/values" + assert.Contains(t, mockTransport.lastRequest.URL.String(), expectedURL) + + for _, expr := range tt.request.Expr { + assert.Contains(t, mockTransport.lastRequest.URL.RawQuery, fmt.Sprintf("expr=%s", url.QueryEscape(expr))) + } + assert.Contains(t, mockTransport.lastRequest.URL.RawQuery, fmt.Sprintf("tag=%s", tt.request.Tag)) + + if tt.request.From != "" { + assert.Contains(t, mockTransport.lastRequest.URL.RawQuery, fmt.Sprintf("from=%s", tt.request.From)) + } + if tt.request.Until != "" { + assert.Contains(t, mockTransport.lastRequest.URL.RawQuery, fmt.Sprintf("until=%s", tt.request.Until)) + } + if tt.request.Limit != 0 { + assert.Contains(t, mockTransport.lastRequest.URL.RawQuery, fmt.Sprintf("limit=%d", tt.request.Limit)) + } + if tt.request.ValuePrefix != "" { + assert.Contains(t, mockTransport.lastRequest.URL.RawQuery, fmt.Sprintf("valuePrefix=%s", tt.request.ValuePrefix)) + } + } + }) + } +} func TestHandleFunctions(t *testing.T) { tests := []struct { name string diff --git a/pkg/tsdb/graphite/types.go b/pkg/tsdb/graphite/types.go index 2e427f1d327..04a3c8e812a 100644 --- a/pkg/tsdb/graphite/types.go +++ b/pkg/tsdb/graphite/types.go @@ -16,7 +16,7 @@ type URLParams struct { SubPath string Method string Body io.Reader - QueryParams map[string]string + QueryParams map[string][]string Headers map[string]string } @@ -66,3 +66,12 @@ type GraphiteTagsRequest struct { Limit int `json:"limit,omitempty"` TagPrefix string `json:"tagPrefix,omitempty"` } + +type GraphiteTagValuesRequest struct { + Expr []string `json:"expr"` + Tag string `json:"tag"` + From string `json:"from"` + Until string `json:"until"` + Limit int `json:"limit,omitempty"` + ValuePrefix string `json:"valuePrefix,omitempty"` +} diff --git a/public/app/plugins/datasource/graphite/datasource.ts b/public/app/plugins/datasource/graphite/datasource.ts index 14b1139f3ad..2c09afd4b83 100644 --- a/public/app/plugins/datasource/graphite/datasource.ts +++ b/public/app/plugins/datasource/graphite/datasource.ts @@ -789,71 +789,6 @@ export class GraphiteDatasource ); } - getTags(optionalOptions: any) { - const options = optionalOptions || {}; - const params: BackendSrvRequest['params'] = {}; - - if (options.range) { - params.from = this.translateTime(options.range.from, false, options.timezone); - params.until = this.translateTime(options.range.to, true, options.timezone); - } - - const httpOptions: BackendSrvRequest = { - method: 'GET', - url: '/tags', - // for cancellations - requestId: options.requestId, - params, - }; - - return lastValueFrom( - this.doGraphiteRequest(httpOptions).pipe( - map((results: FetchResponse) => { - return _map(results.data, (tag) => { - return { - text: tag.tag, - id: tag.id, - }; - }); - }) - ) - ); - } - - getTagValues(options: any = {}) { - const params: BackendSrvRequest['params'] = {}; - - if (options.range) { - params.from = this.translateTime(options.range.from, false, options.timezone); - params.until = this.translateTime(options.range.to, true, options.timezone); - } - - const httpOptions: BackendSrvRequest = { - method: 'GET', - url: '/tags/' + this.templateSrv.replace(options.key), - // for cancellations - requestId: options.requestId, - params, - }; - - return lastValueFrom( - this.doGraphiteRequest(httpOptions).pipe( - map((results: FetchResponse) => { - if (results.data && results.data.values) { - return _map(results.data.values, (value) => { - return { - text: value.value, - id: value.id, - }; - }); - } else { - return []; - } - }) - ) - ); - } - async getTagsAutoComplete(expressions: string[], tagPrefix?: string, optionalOptions?: any) { const options = optionalOptions || {}; const params: BackendSrvRequest['params'] = { @@ -894,7 +829,7 @@ export class GraphiteDatasource return lastValueFrom(this.doGraphiteRequest(httpOptions).pipe(mapToTags())); } - getTagValuesAutoComplete(expressions: string[], tag: string, valuePrefix?: string, optionalOptions?: any) { + async getTagValuesAutoComplete(expressions: string[], tag: string, valuePrefix?: string, optionalOptions?: any) { const options = optionalOptions || {}; const params: BackendSrvRequest['params'] = { expr: _map(expressions, (expression) => this.templateSrv.replace((expression || '').trim())), @@ -911,6 +846,20 @@ export class GraphiteDatasource params.until = this.translateTime(options.range.to, true, options.timezone); } + if (config.featureToggles.graphiteBackendMode) { + const tagValues = await this.postResource('tags/autoComplete/values', { + from: typeof params.from === 'string' ? params.from : `${params.from}`, + until: typeof params.until === 'string' ? params.until : `${params.until}`, + expr: params.expr, + tag: params.tag, + valuePrefix, + limit: options.limit, + }); + return tagValues.map((tag) => ({ + text: tag, + })); + } + const httpOptions: BackendSrvRequest = { method: 'GET', url: '/tags/autoComplete/values', From 172febd690db7f5dc78a804384fbfbc4d23dd8f7 Mon Sep 17 00:00:00 2001 From: Bogdan Matei Date: Mon, 15 Sep 2025 13:41:06 +0300 Subject: [PATCH 25/33] Dashboard: Do not issue queries for panels outside the viewport (#111067) --- package.json | 4 ++-- .../datasource/dashboard/datasource.ts | 14 ++++++++++-- yarn.lock | 22 +++++++++---------- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 22095887212..b269774ebd8 100644 --- a/package.json +++ b/package.json @@ -286,8 +286,8 @@ "@grafana/plugin-ui": "^0.10.10", "@grafana/prometheus": "workspace:*", "@grafana/runtime": "workspace:*", - "@grafana/scenes": "6.35.0", - "@grafana/scenes-react": "6.35.0", + "@grafana/scenes": "6.35.3", + "@grafana/scenes-react": "6.35.3", "@grafana/schema": "workspace:*", "@grafana/sql": "workspace:*", "@grafana/ui": "workspace:*", diff --git a/public/app/plugins/datasource/dashboard/datasource.ts b/public/app/plugins/datasource/dashboard/datasource.ts index 2386418231a..8f945bad984 100644 --- a/public/app/plugins/datasource/dashboard/datasource.ts +++ b/public/app/plugins/datasource/dashboard/datasource.ts @@ -87,7 +87,13 @@ export class DashboardDatasource extends DataSourceApi { sourceDataProvider?.setContainerWidth(500); } - const cleanUp = activateSceneObjectAndParentTree(sourceDataProvider!); + /** + * Ignore the isInView flag on the original data provider + * This allows queries to be run even if the original datasource is outside the viewport + */ + sourceDataProvider?.bypassIsInViewChanged?.(true); + + const activateCleanUp = activateSceneObjectAndParentTree(sourceDataProvider!); return sourceDataProvider!.getResultsStream!().pipe( debounceTime(50), @@ -101,7 +107,11 @@ export class DashboardDatasource extends DataSourceApi { }; }), this.emitFirstLoadedDataIfMixedDS(options.requestId), - finalize(() => cleanUp?.()) + finalize(() => { + sourceDataProvider?.bypassIsInViewChanged?.(false); + + activateCleanUp?.(); + }) ); }); } diff --git a/yarn.lock b/yarn.lock index 55a99589c60..4a0e5b58b1e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3521,11 +3521,11 @@ __metadata: languageName: unknown linkType: soft -"@grafana/scenes-react@npm:6.35.0": - version: 6.35.0 - resolution: "@grafana/scenes-react@npm:6.35.0" +"@grafana/scenes-react@npm:6.35.3": + version: 6.35.3 + resolution: "@grafana/scenes-react@npm:6.35.3" dependencies: - "@grafana/scenes": "npm:6.35.0" + "@grafana/scenes": "npm:6.35.3" lru-cache: "npm:^10.2.2" react-use: "npm:^17.4.0" peerDependencies: @@ -3537,13 +3537,13 @@ __metadata: react: ^18.0.0 react-dom: ^18.0.0 react-router-dom: ^6.28.0 - checksum: 10/7f4f3b2c23137adb0dc71b2fe93e92c41e2f71fc91d1f7a1432f04946841220dc27941f021af74642ebc3b58467c482e1349b8b7e0d490b78f3a33f8671fda85 + checksum: 10/2c220eef6aa8560d073a27a02918b4bbf4d0cffee273ff597e94eb4376e75d0e47fcdaa2cd1ca8ea851c29477722dec2e2d2f817f2c5c185151de2051c5e2ab7 languageName: node linkType: hard -"@grafana/scenes@npm:6.35.0": - version: 6.35.0 - resolution: "@grafana/scenes@npm:6.35.0" +"@grafana/scenes@npm:6.35.3": + version: 6.35.3 + resolution: "@grafana/scenes@npm:6.35.3" dependencies: "@floating-ui/react": "npm:^0.26.16" "@leeoniya/ufuzzy": "npm:^1.0.16" @@ -3563,7 +3563,7 @@ __metadata: react: ^18.0.0 react-dom: ^18.0.0 react-router-dom: ^6.28.0 - checksum: 10/4a9ff88c2a6cfafe5aaa7d50b8ee67ab38f105b3073cab4469665338e28c9991fb9b908c0325680cc32525a87ebcd34c3d376d940a4f9b9b684c8db238f02527 + checksum: 10/a209e6a1cbe6c4e70c2cd8c413df2560a59b157944888e317385f19e43e23ff826d84c922618b1268a7085278aca45a369c8f6255bf70a06134622c6ef2a542e languageName: node linkType: hard @@ -18168,8 +18168,8 @@ __metadata: "@grafana/plugin-ui": "npm:^0.10.10" "@grafana/prometheus": "workspace:*" "@grafana/runtime": "workspace:*" - "@grafana/scenes": "npm:6.35.0" - "@grafana/scenes-react": "npm:6.35.0" + "@grafana/scenes": "npm:6.35.3" + "@grafana/scenes-react": "npm:6.35.3" "@grafana/schema": "workspace:*" "@grafana/sql": "workspace:*" "@grafana/test-utils": "workspace:*" From 294fd943c041f89f5ef25c01a0719e6cbef68058 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Mon, 15 Sep 2025 12:45:15 +0200 Subject: [PATCH 26/33] Chore: Update authlib (#110880) * Chore: Update authlib * exclude incompatible version of github.com/grafana/gomemcache * Update go-jose to v4 * fix jose imports * remove jose v3 from go.mod * fix tests * fix serialize * fix failing live tests * add v1 of ES256 testkeys. Port tests to use ES256 instead of HS256 * accept more signature algs for okta and azuread * azure social graph token sig * accept more signature algs for oauth refresh and jwt auth * update workspace * add a static signer for inproc * rebase and fix ext_jwt * fix jwt tests * apply alex patch on gomemcache * update linting * fix ext_jwt panic * update workspaces --------- Co-authored-by: Jo Garnier --- apps/alerting/notifications/go.mod | 4 +- apps/alerting/notifications/go.sum | 8 +-- apps/alerting/rules/go.mod | 4 +- apps/alerting/rules/go.sum | 8 +-- apps/dashboard/go.mod | 12 ++-- apps/dashboard/go.sum | 54 ++++-------------- apps/investigations/go.mod | 4 +- apps/investigations/go.sum | 8 +-- apps/playlist/go.mod | 4 +- apps/playlist/go.sum | 8 +-- apps/plugins/go.mod | 12 ++-- apps/plugins/go.sum | 55 ++++--------------- apps/provisioning/go.mod | 10 ++-- apps/provisioning/go.sum | 18 +++--- apps/secret/go.mod | 2 +- apps/secret/go.sum | 4 +- apps/shorturl/go.mod | 4 +- apps/shorturl/go.sum | 8 +-- go.mod | 17 +++--- go.sum | 26 ++++----- go.work.sum | 45 +++++++++++++++ pkg/aggregator/go.mod | 4 +- pkg/aggregator/go.sum | 8 +-- pkg/apimachinery/go.mod | 12 ++-- pkg/apimachinery/go.sum | 51 ++++------------- pkg/apimachinery/identity/requester.go | 5 +- pkg/apimachinery/identity/requester_test.go | 38 +++++++++++-- pkg/apiserver/go.mod | 12 ++-- pkg/apiserver/go.sum | 53 ++++-------------- pkg/build/go.mod | 4 +- pkg/build/go.sum | 8 +-- pkg/extensions/enterprise_imports.go | 5 +- pkg/login/social/connectors/azuread_oauth.go | 8 +-- .../social/connectors/azuread_oauth_test.go | 12 ++-- .../social/connectors/google_oauth_test.go | 37 +++++++++++-- pkg/login/social/connectors/okta_oauth.go | 7 ++- pkg/promlib/go.mod | 6 +- pkg/promlib/go.sum | 12 ++-- pkg/registry/apis/folders/authorizer_test.go | 2 +- pkg/services/auth/idimpl/service.go | 5 +- pkg/services/auth/idimpl/service_test.go | 41 ++++++++++++-- pkg/services/auth/idimpl/signer.go | 6 +- pkg/services/auth/jwt/auth.go | 9 ++- pkg/services/auth/jwt/auth_test.go | 8 +-- pkg/services/auth/jwt/key_sets.go | 2 +- pkg/services/auth/jwt/rsa_keys_test.go | 2 +- pkg/services/auth/jwt/signing_test.go | 10 ++-- pkg/services/auth/jwt/validation.go | 6 +- pkg/services/authn/clients/ext_jwt.go | 5 +- pkg/services/authn/clients/ext_jwt_test.go | 52 ++++++++++++------ pkg/services/authz/rbac/service_test.go | 2 +- pkg/services/live/live_test.go | 41 ++++++++++++-- pkg/services/oauthtoken/oauth_token.go | 7 ++- pkg/services/signingkeys/signingkeys.go | 2 +- .../signingkeys/signingkeysimpl/service.go | 2 +- .../signingkeysimpl/service_test.go | 2 +- .../signingkeys/signingkeystest/fake.go | 2 +- .../signingkeys/signingkeystore/fake.go | 3 +- pkg/storage/unified/apistore/managed_test.go | 3 +- pkg/storage/unified/resource/client.go | 40 +++++++++----- .../unified/sql/test/integration_test.go | 2 +- .../unified/testing/storage_backend.go | 2 +- 62 files changed, 470 insertions(+), 383 deletions(-) diff --git a/apps/alerting/notifications/go.mod b/apps/alerting/notifications/go.mod index 61ff43d776e..3978235e7ec 100644 --- a/apps/alerting/notifications/go.mod +++ b/apps/alerting/notifications/go.mod @@ -93,8 +93,8 @@ require ( golang.org/x/tools v0.37.0 // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect google.golang.org/grpc v1.75.0 // indirect google.golang.org/protobuf v1.36.8 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect diff --git a/apps/alerting/notifications/go.sum b/apps/alerting/notifications/go.sum index ffc862de007..e462917df50 100644 --- a/apps/alerting/notifications/go.sum +++ b/apps/alerting/notifications/go.sum @@ -327,10 +327,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-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4= google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s= -google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY= -google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc= +google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 h1:d8Nakh1G+ur7+P3GcMjpRDEkoLUcLW2iU92XVqR+XMQ= +google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090/go.mod h1:U8EXRNSd8sUYyDfs/It7KVWodQr+Hf9xtxyxWudSwEw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 h1:/OQuEa4YWtDt7uQWHd3q3sUMb+QOLQUg1xa8CEsRv5w= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og= 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/apps/alerting/rules/go.mod b/apps/alerting/rules/go.mod index eb8e64deadc..7a7b3cd36b1 100644 --- a/apps/alerting/rules/go.mod +++ b/apps/alerting/rules/go.mod @@ -73,8 +73,8 @@ require ( golang.org/x/time v0.13.0 // indirect golang.org/x/tools v0.37.0 // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect google.golang.org/grpc v1.75.0 // indirect google.golang.org/protobuf v1.36.8 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/apps/alerting/rules/go.sum b/apps/alerting/rules/go.sum index 76a2bd7967f..af8e905e2a0 100644 --- a/apps/alerting/rules/go.sum +++ b/apps/alerting/rules/go.sum @@ -197,10 +197,10 @@ gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0 gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY= -google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc= +google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 h1:d8Nakh1G+ur7+P3GcMjpRDEkoLUcLW2iU92XVqR+XMQ= +google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090/go.mod h1:U8EXRNSd8sUYyDfs/It7KVWodQr+Hf9xtxyxWudSwEw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 h1:/OQuEa4YWtDt7uQWHd3q3sUMb+QOLQUg1xa8CEsRv5w= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og= google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= diff --git a/apps/dashboard/go.mod b/apps/dashboard/go.mod index ae3e42bf657..43be71ae00b 100644 --- a/apps/dashboard/go.mod +++ b/apps/dashboard/go.mod @@ -4,7 +4,7 @@ go 1.24.6 require ( cuelang.org/go v0.11.1 - github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43 + github.com/grafana/authlib/types v0.0.0-20250721184729-1593a38e4933 github.com/grafana/grafana-app-sdk v0.40.3 github.com/grafana/grafana-app-sdk/logging v0.40.3 github.com/grafana/grafana-plugin-sdk-go v0.278.0 @@ -36,7 +36,7 @@ require ( github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/getkin/kin-openapi v0.132.0 // indirect - github.com/go-jose/go-jose/v3 v3.0.4 // indirect + github.com/go-jose/go-jose/v4 v4.1.2 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect @@ -52,8 +52,8 @@ require ( github.com/google/go-cmp v0.7.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/mux v1.8.1 // indirect - github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43 // indirect - github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 // indirect + github.com/grafana/authlib v0.0.0-20250909101823-1b466dbd19a1 // indirect + github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 // indirect github.com/grafana/otel-profiling-go v0.5.1 // indirect github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect @@ -131,8 +131,8 @@ require ( golang.org/x/time v0.13.0 // indirect golang.org/x/tools v0.37.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect google.golang.org/grpc v1.75.0 // indirect google.golang.org/protobuf v1.36.8 // indirect gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect diff --git a/apps/dashboard/go.sum b/apps/dashboard/go.sum index fc5e8091c90..4c98092d2bf 100644 --- a/apps/dashboard/go.sum +++ b/apps/dashboard/go.sum @@ -54,8 +54,8 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/getkin/kin-openapi v0.132.0 h1:3ISeLMsQzcb5v26yeJrBcdTCEQTag36ZjaGk7MIRUwk= github.com/getkin/kin-openapi v0.132.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= -github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY= -github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= +github.com/go-jose/go-jose/v4 v4.1.2 h1:TK/7NqRQZfgAh+Td8AlsrvtPoUyiHh0LqVvokh+1vHI= +github.com/go-jose/go-jose/v4 v4.1.2/go.mod h1:22cg9HWM1pOlnRiY+9cQYJ9XHmya1bYW8OeDM6Ku6Oo= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= @@ -98,12 +98,12 @@ github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25d github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43 h1:vVPT0i5Y1vI6qzecYStV2yk7cHKrC3Pc7AgvwT5KydQ= -github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43/go.mod h1:1fWkOiL+m32NBgRHZtlZGz2ji868tPZACYbqP3nBRJI= -github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43 h1:NlkGMnVi/oUn6Cr90QbJYpQJ4FnjyAIG9Ex5GtTZIzw= -github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw= -github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 h1:qcSGhr691f1mmPHwg2svGyO40Ex92G02aOyHzP6XHCE= -github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914/go.mod h1:OiN4P4aC6LwLzLbEupH3Ue83VfQoNMfG48rsna8jI/E= +github.com/grafana/authlib v0.0.0-20250909101823-1b466dbd19a1 h1:qdH5s5FV+0Dyja8O1tBJq7MGd8nPCfxfsMimcYq5cRI= +github.com/grafana/authlib v0.0.0-20250909101823-1b466dbd19a1/go.mod h1:C6CmTG6vfiqebjJswKsc6zes+1F/OtTCi6aAtL5Um6A= +github.com/grafana/authlib/types v0.0.0-20250721184729-1593a38e4933 h1:GjiMR5NIO1/bYSCnt8x7VUeOMaupv2qXJkeLDVAddxQ= +github.com/grafana/authlib/types v0.0.0-20250721184729-1593a38e4933/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw= +github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 h1:jSojuc7njleS3UOz223WDlXOinmuLAIPI0z2vtq8EgI= +github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4/go.mod h1:VahT+GtfQIM+o8ht2StR6J9g+Ef+C2Vokh5uuSmOD/4= github.com/grafana/grafana-app-sdk v0.40.3 h1:JFo7uAfbAJUfZ9neD7/4sODKm1xgu9zhckclH/N4DYU= github.com/grafana/grafana-app-sdk v0.40.3/go.mod h1:j0KzHo3Sa6kd+lnwSScBNoV9Vobkg/YY9HtEjxpyPrk= github.com/grafana/grafana-app-sdk/logging v0.40.3 h1:2VXsXXEQiqAavRP8wusRDB6rDqf5lufP7A6NfjELqPE= @@ -256,7 +256,6 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -280,7 +279,6 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= @@ -326,26 +324,18 @@ go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= golang.org/x/exp v0.0.0-20250911091902-df9299821621 h1:2id6c1/gto0kaHYyrixvknJ8tUK/Qs5IsmBtrc+FtgU= golang.org/x/exp v0.0.0-20250911091902-df9299821621/go.mod h1:TwQYMMnGpvZyc+JpB/UAuTNIsVJifOlSkrZkhcvpVUk= 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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= @@ -353,8 +343,6 @@ golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKl golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -363,35 +351,19 @@ golang.org/x/sys v0.0.0-20191020152052-9984515f0562/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053 h1:dHQOQddU4YHS5gY33/6klKjq7Gp3WwMyOXGNp5nzRj8= golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053/go.mod h1:+nZKN+XVh4LCiA9DV3ywrzN4gumyCnKjau3NGb9SGoE= -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.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ= golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI= @@ -400,8 +372,6 @@ 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.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -412,10 +382,10 @@ golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhS golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY= -google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc= +google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 h1:d8Nakh1G+ur7+P3GcMjpRDEkoLUcLW2iU92XVqR+XMQ= +google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090/go.mod h1:U8EXRNSd8sUYyDfs/It7KVWodQr+Hf9xtxyxWudSwEw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 h1:/OQuEa4YWtDt7uQWHd3q3sUMb+QOLQUg1xa8CEsRv5w= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og= google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= diff --git a/apps/investigations/go.mod b/apps/investigations/go.mod index dea9bea5fe2..7da519c3172 100644 --- a/apps/investigations/go.mod +++ b/apps/investigations/go.mod @@ -74,8 +74,8 @@ require ( golang.org/x/time v0.13.0 // indirect golang.org/x/tools v0.37.0 // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect google.golang.org/grpc v1.75.0 // indirect google.golang.org/protobuf v1.36.8 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/apps/investigations/go.sum b/apps/investigations/go.sum index 76a2bd7967f..af8e905e2a0 100644 --- a/apps/investigations/go.sum +++ b/apps/investigations/go.sum @@ -197,10 +197,10 @@ gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0 gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY= -google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc= +google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 h1:d8Nakh1G+ur7+P3GcMjpRDEkoLUcLW2iU92XVqR+XMQ= +google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090/go.mod h1:U8EXRNSd8sUYyDfs/It7KVWodQr+Hf9xtxyxWudSwEw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 h1:/OQuEa4YWtDt7uQWHd3q3sUMb+QOLQUg1xa8CEsRv5w= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og= google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= diff --git a/apps/playlist/go.mod b/apps/playlist/go.mod index e98a1b67deb..0a24d0332ca 100644 --- a/apps/playlist/go.mod +++ b/apps/playlist/go.mod @@ -74,8 +74,8 @@ require ( golang.org/x/time v0.13.0 // indirect golang.org/x/tools v0.37.0 // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect google.golang.org/grpc v1.75.0 // indirect google.golang.org/protobuf v1.36.8 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/apps/playlist/go.sum b/apps/playlist/go.sum index 76a2bd7967f..af8e905e2a0 100644 --- a/apps/playlist/go.sum +++ b/apps/playlist/go.sum @@ -197,10 +197,10 @@ gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0 gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY= -google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc= +google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 h1:d8Nakh1G+ur7+P3GcMjpRDEkoLUcLW2iU92XVqR+XMQ= +google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090/go.mod h1:U8EXRNSd8sUYyDfs/It7KVWodQr+Hf9xtxyxWudSwEw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 h1:/OQuEa4YWtDt7uQWHd3q3sUMb+QOLQUg1xa8CEsRv5w= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og= google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= diff --git a/apps/plugins/go.mod b/apps/plugins/go.mod index b5f72f7c26d..6d6b2695238 100644 --- a/apps/plugins/go.mod +++ b/apps/plugins/go.mod @@ -3,7 +3,7 @@ module github.com/grafana/grafana/apps/plugins go 1.24.4 require ( - github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43 + github.com/grafana/authlib/types v0.0.0-20250721184729-1593a38e4933 github.com/grafana/grafana-app-sdk v0.40.3 github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250428110029-a8ea72012bde k8s.io/apimachinery v0.33.3 @@ -22,7 +22,7 @@ require ( github.com/evanphx/json-patch v5.9.11+incompatible // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/getkin/kin-openapi v0.132.0 // indirect - github.com/go-jose/go-jose/v3 v3.0.4 // indirect + github.com/go-jose/go-jose/v4 v4.1.2 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect @@ -34,8 +34,8 @@ require ( github.com/google/go-cmp v0.7.0 // indirect github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43 // indirect - github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 // indirect + github.com/grafana/authlib v0.0.0-20250909101823-1b466dbd19a1 // indirect + github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 // indirect github.com/grafana/grafana-app-sdk/logging v0.40.3 // indirect github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect @@ -81,8 +81,8 @@ require ( golang.org/x/time v0.13.0 // indirect golang.org/x/tools v0.37.0 // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect google.golang.org/grpc v1.75.0 // indirect google.golang.org/protobuf v1.36.8 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/apps/plugins/go.sum b/apps/plugins/go.sum index 6d4958058a4..91b713139c5 100644 --- a/apps/plugins/go.sum +++ b/apps/plugins/go.sum @@ -20,8 +20,8 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/getkin/kin-openapi v0.132.0 h1:3ISeLMsQzcb5v26yeJrBcdTCEQTag36ZjaGk7MIRUwk= github.com/getkin/kin-openapi v0.132.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= -github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY= -github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= +github.com/go-jose/go-jose/v4 v4.1.2 h1:TK/7NqRQZfgAh+Td8AlsrvtPoUyiHh0LqVvokh+1vHI= +github.com/go-jose/go-jose/v4 v4.1.2/go.mod h1:22cg9HWM1pOlnRiY+9cQYJ9XHmya1bYW8OeDM6Ku6Oo= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -51,12 +51,12 @@ github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43 h1:vVPT0i5Y1vI6qzecYStV2yk7cHKrC3Pc7AgvwT5KydQ= -github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43/go.mod h1:1fWkOiL+m32NBgRHZtlZGz2ji868tPZACYbqP3nBRJI= -github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43 h1:NlkGMnVi/oUn6Cr90QbJYpQJ4FnjyAIG9Ex5GtTZIzw= -github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw= -github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 h1:qcSGhr691f1mmPHwg2svGyO40Ex92G02aOyHzP6XHCE= -github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914/go.mod h1:OiN4P4aC6LwLzLbEupH3Ue83VfQoNMfG48rsna8jI/E= +github.com/grafana/authlib v0.0.0-20250909101823-1b466dbd19a1 h1:qdH5s5FV+0Dyja8O1tBJq7MGd8nPCfxfsMimcYq5cRI= +github.com/grafana/authlib v0.0.0-20250909101823-1b466dbd19a1/go.mod h1:C6CmTG6vfiqebjJswKsc6zes+1F/OtTCi6aAtL5Um6A= +github.com/grafana/authlib/types v0.0.0-20250721184729-1593a38e4933 h1:GjiMR5NIO1/bYSCnt8x7VUeOMaupv2qXJkeLDVAddxQ= +github.com/grafana/authlib/types v0.0.0-20250721184729-1593a38e4933/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw= +github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 h1:jSojuc7njleS3UOz223WDlXOinmuLAIPI0z2vtq8EgI= +github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4/go.mod h1:VahT+GtfQIM+o8ht2StR6J9g+Ef+C2Vokh5uuSmOD/4= github.com/grafana/grafana-app-sdk v0.40.3 h1:JFo7uAfbAJUfZ9neD7/4sODKm1xgu9zhckclH/N4DYU= github.com/grafana/grafana-app-sdk v0.40.3/go.mod h1:j0KzHo3Sa6kd+lnwSScBNoV9Vobkg/YY9HtEjxpyPrk= github.com/grafana/grafana-app-sdk/logging v0.40.3 h1:2VXsXXEQiqAavRP8wusRDB6rDqf5lufP7A6NfjELqPE= @@ -132,7 +132,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= @@ -141,7 +140,6 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= @@ -171,22 +169,14 @@ go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= 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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= @@ -194,35 +184,17 @@ golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKl golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -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.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ= golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI= @@ -231,8 +203,6 @@ 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.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -243,10 +213,10 @@ gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0 gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY= -google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc= +google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 h1:d8Nakh1G+ur7+P3GcMjpRDEkoLUcLW2iU92XVqR+XMQ= +google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090/go.mod h1:U8EXRNSd8sUYyDfs/It7KVWodQr+Hf9xtxyxWudSwEw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 h1:/OQuEa4YWtDt7uQWHd3q3sUMb+QOLQUg1xa8CEsRv5w= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og= google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= @@ -260,7 +230,6 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/api v0.33.3 h1:SRd5t//hhkI1buzxb288fy2xvjubstenEKL9K51KBI8= diff --git a/apps/provisioning/go.mod b/apps/provisioning/go.mod index 40f81709622..25ad4d8d1ee 100644 --- a/apps/provisioning/go.mod +++ b/apps/provisioning/go.mod @@ -5,7 +5,7 @@ go 1.24.6 require ( github.com/google/go-github/v70 v70.0.0 github.com/google/uuid v1.6.0 - github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43 + github.com/grafana/authlib v0.0.0-20250909101823-1b466dbd19a1 github.com/grafana/grafana-app-sdk/logging v0.40.3 github.com/grafana/grafana/apps/secret v0.0.0-20250902093454-b56b7add012f github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250804150913-990f1c69ecc2 @@ -28,6 +28,7 @@ require ( github.com/emicklei/go-restful/v3 v3.12.1 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-jose/go-jose/v3 v3.0.4 // indirect + github.com/go-jose/go-jose/v4 v4.1.2 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect @@ -39,8 +40,8 @@ require ( github.com/google/go-github/v64 v64.0.0 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/gorilla/mux v1.8.1 // indirect - github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43 // indirect - github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 // indirect + github.com/grafana/authlib/types v0.0.0-20250721184729-1593a38e4933 // indirect + github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 // indirect github.com/grafana/grafana-app-sdk v0.40.3 // indirect github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect github.com/josharian/intern v1.0.0 // indirect @@ -63,7 +64,6 @@ require ( go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/otel v1.38.0 // indirect go.opentelemetry.io/otel/metric v1.38.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect go.opentelemetry.io/otel/trace v1.38.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect @@ -75,7 +75,7 @@ require ( golang.org/x/text v0.29.0 // indirect golang.org/x/time v0.13.0 // indirect golang.org/x/tools v0.37.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect google.golang.org/grpc v1.75.0 // indirect google.golang.org/protobuf v1.36.8 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect diff --git a/apps/provisioning/go.sum b/apps/provisioning/go.sum index 4c00598b5de..a8f0d31a3fc 100644 --- a/apps/provisioning/go.sum +++ b/apps/provisioning/go.sum @@ -14,6 +14,8 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY= github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= +github.com/go-jose/go-jose/v4 v4.1.2 h1:TK/7NqRQZfgAh+Td8AlsrvtPoUyiHh0LqVvokh+1vHI= +github.com/go-jose/go-jose/v4 v4.1.2/go.mod h1:22cg9HWM1pOlnRiY+9cQYJ9XHmya1bYW8OeDM6Ku6Oo= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -50,12 +52,12 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43 h1:vVPT0i5Y1vI6qzecYStV2yk7cHKrC3Pc7AgvwT5KydQ= -github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43/go.mod h1:1fWkOiL+m32NBgRHZtlZGz2ji868tPZACYbqP3nBRJI= -github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43 h1:NlkGMnVi/oUn6Cr90QbJYpQJ4FnjyAIG9Ex5GtTZIzw= -github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw= -github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 h1:qcSGhr691f1mmPHwg2svGyO40Ex92G02aOyHzP6XHCE= -github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914/go.mod h1:OiN4P4aC6LwLzLbEupH3Ue83VfQoNMfG48rsna8jI/E= +github.com/grafana/authlib v0.0.0-20250909101823-1b466dbd19a1 h1:qdH5s5FV+0Dyja8O1tBJq7MGd8nPCfxfsMimcYq5cRI= +github.com/grafana/authlib v0.0.0-20250909101823-1b466dbd19a1/go.mod h1:C6CmTG6vfiqebjJswKsc6zes+1F/OtTCi6aAtL5Um6A= +github.com/grafana/authlib/types v0.0.0-20250721184729-1593a38e4933 h1:GjiMR5NIO1/bYSCnt8x7VUeOMaupv2qXJkeLDVAddxQ= +github.com/grafana/authlib/types v0.0.0-20250721184729-1593a38e4933/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw= +github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 h1:jSojuc7njleS3UOz223WDlXOinmuLAIPI0z2vtq8EgI= +github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4/go.mod h1:VahT+GtfQIM+o8ht2StR6J9g+Ef+C2Vokh5uuSmOD/4= github.com/grafana/grafana-app-sdk v0.40.3 h1:JFo7uAfbAJUfZ9neD7/4sODKm1xgu9zhckclH/N4DYU= github.com/grafana/grafana-app-sdk v0.40.3/go.mod h1:j0KzHo3Sa6kd+lnwSScBNoV9Vobkg/YY9HtEjxpyPrk= github.com/grafana/grafana-app-sdk/logging v0.40.3 h1:2VXsXXEQiqAavRP8wusRDB6rDqf5lufP7A6NfjELqPE= @@ -219,8 +221,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= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 h1:/OQuEa4YWtDt7uQWHd3q3sUMb+QOLQUg1xa8CEsRv5w= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og= google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= diff --git a/apps/secret/go.mod b/apps/secret/go.mod index 3eafe5927c3..76e3e328f88 100644 --- a/apps/secret/go.mod +++ b/apps/secret/go.mod @@ -61,7 +61,7 @@ require ( golang.org/x/term v0.35.0 // indirect golang.org/x/text v0.29.0 // indirect golang.org/x/time v0.13.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/client-go v0.33.3 // indirect diff --git a/apps/secret/go.sum b/apps/secret/go.sum index 0f905737469..8c6105dd642 100644 --- a/apps/secret/go.sum +++ b/apps/secret/go.sum @@ -166,8 +166,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= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 h1:/OQuEa4YWtDt7uQWHd3q3sUMb+QOLQUg1xa8CEsRv5w= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og= google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= diff --git a/apps/shorturl/go.mod b/apps/shorturl/go.mod index a015415473c..3c0e4a7ed93 100644 --- a/apps/shorturl/go.mod +++ b/apps/shorturl/go.mod @@ -75,8 +75,8 @@ require ( golang.org/x/time v0.13.0 // indirect golang.org/x/tools v0.37.0 // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect google.golang.org/grpc v1.75.0 // indirect google.golang.org/protobuf v1.36.8 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/apps/shorturl/go.sum b/apps/shorturl/go.sum index 76a2bd7967f..af8e905e2a0 100644 --- a/apps/shorturl/go.sum +++ b/apps/shorturl/go.sum @@ -197,10 +197,10 @@ gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0 gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY= -google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc= +google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 h1:d8Nakh1G+ur7+P3GcMjpRDEkoLUcLW2iU92XVqR+XMQ= +google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090/go.mod h1:U8EXRNSd8sUYyDfs/It7KVWodQr+Hf9xtxyxWudSwEw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 h1:/OQuEa4YWtDt7uQWHd3q3sUMb+QOLQUg1xa8CEsRv5w= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og= google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= diff --git a/go.mod b/go.mod index afbe29920dd..c095043e143 100644 --- a/go.mod +++ b/go.mod @@ -59,8 +59,7 @@ require ( github.com/fullstorydev/grpchan v1.1.1 // @grafana/grafana-backend-group github.com/gchaincl/sqlhooks v1.3.0 // @grafana/grafana-search-and-storage github.com/getkin/kin-openapi v0.132.0 // @grafana/grafana-app-platform-squad - github.com/go-jose/go-jose/v3 v3.0.4 // @grafana/identity-access-team - github.com/go-jose/go-jose/v4 v4.1.1 // indirect; @grafana/identity-access-team + github.com/go-jose/go-jose/v4 v4.1.2 // @grafana/identity-access-team github.com/go-kit/log v0.2.1 // @grafana/grafana-backend-group github.com/go-ldap/ldap/v3 v3.4.4 // @grafana/identity-access-team github.com/go-logfmt/logfmt v0.6.0 // @grafana/oss-big-tent @@ -87,11 +86,11 @@ require ( github.com/gorilla/mux v1.8.1 // @grafana/grafana-backend-group github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // @grafana/grafana-app-platform-squad github.com/grafana/alerting v0.0.0-20250912123435-f2728ab090ee // @grafana/alerting-backend - github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43 // @grafana/identity-access-team - github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43 // @grafana/identity-access-team + github.com/grafana/authlib v0.0.0-20250909101823-1b466dbd19a1 // @grafana/identity-access-team + github.com/grafana/authlib/types v0.0.0-20250721184729-1593a38e4933 // @grafana/identity-access-team github.com/grafana/dataplane/examples v0.0.1 // @grafana/observability-metrics github.com/grafana/dataplane/sdata v0.0.9 // @grafana/observability-metrics - github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 // @grafana/grafana-backend-group + github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 // @grafana/grafana-backend-group github.com/grafana/e2e v0.1.1 // @grafana-app-platform-squad github.com/grafana/gofpdf v0.0.0-20250307124105-3b9c5d35577f // @grafana/sharing-squad github.com/grafana/gomemcache v0.0.0-20250318131618-74242eea118d // @grafana/grafana-operator-experience-squad @@ -624,8 +623,8 @@ require ( golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect @@ -644,6 +643,7 @@ require ( ) require ( + github.com/gomodule/redigo v1.8.9 // indirect github.com/gopherjs/gopherjs v1.17.2 // indirect github.com/smarty/assertions v1.15.0 // indirect golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053 // indirect @@ -674,3 +674,6 @@ exclude k8s.io/client-go v12.0.0+incompatible // only used to run tests and not required for building the Grafana binary. // Since the test data doesn't contain a license file we exclude it. exclude github.com/RoaringBitmap/real-roaring-datasets v0.0.0-20190726190000-eb7c87156f76 + +// gomemcache 20250828162811 contains breaking changes, so it needs to be excluded unless loki package is updated +exclude github.com/grafana/gomemcache v0.0.0-20250828162811-a96f6acee2fe diff --git a/go.sum b/go.sum index 8447e63cc44..5ed95314a2f 100644 --- a/go.sum +++ b/go.sum @@ -1226,10 +1226,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY= -github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= -github.com/go-jose/go-jose/v4 v4.1.1 h1:JYhSgy4mXXzAdF3nUx3ygx347LRXJRrpgyU3adRmkAI= -github.com/go-jose/go-jose/v4 v4.1.1/go.mod h1:BdsZGqgdO3b6tTc6LSE56wcDbMMLuPsw5d4ZD5f94kA= +github.com/go-jose/go-jose/v4 v4.1.2 h1:TK/7NqRQZfgAh+Td8AlsrvtPoUyiHh0LqVvokh+1vHI= +github.com/go-jose/go-jose/v4 v4.1.2/go.mod h1:22cg9HWM1pOlnRiY+9cQYJ9XHmya1bYW8OeDM6Ku6Oo= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= @@ -1592,16 +1590,16 @@ github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5T github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= github.com/grafana/alerting v0.0.0-20250912123435-f2728ab090ee h1:J/9l2w3Q5JDBEB3t5bDsxhxWldGtFd5KpYGoQi0m/hc= github.com/grafana/alerting v0.0.0-20250912123435-f2728ab090ee/go.mod h1:XWqj/rlsy4OV/E9XNNyFn+a7U4GNsSugPb2rDBj9+58= -github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43 h1:vVPT0i5Y1vI6qzecYStV2yk7cHKrC3Pc7AgvwT5KydQ= -github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43/go.mod h1:1fWkOiL+m32NBgRHZtlZGz2ji868tPZACYbqP3nBRJI= -github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43 h1:NlkGMnVi/oUn6Cr90QbJYpQJ4FnjyAIG9Ex5GtTZIzw= -github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw= +github.com/grafana/authlib v0.0.0-20250909101823-1b466dbd19a1 h1:qdH5s5FV+0Dyja8O1tBJq7MGd8nPCfxfsMimcYq5cRI= +github.com/grafana/authlib v0.0.0-20250909101823-1b466dbd19a1/go.mod h1:C6CmTG6vfiqebjJswKsc6zes+1F/OtTCi6aAtL5Um6A= +github.com/grafana/authlib/types v0.0.0-20250721184729-1593a38e4933 h1:GjiMR5NIO1/bYSCnt8x7VUeOMaupv2qXJkeLDVAddxQ= +github.com/grafana/authlib/types v0.0.0-20250721184729-1593a38e4933/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw= github.com/grafana/dataplane/examples v0.0.1 h1:K9M5glueWyLoL4//H+EtTQq16lXuHLmOhb6DjSCahzA= github.com/grafana/dataplane/examples v0.0.1/go.mod h1:h5YwY8s407/17XF5/dS8XrUtsTVV2RnuW8+m1Mp46mg= github.com/grafana/dataplane/sdata v0.0.9 h1:AGL1LZnCUG4MnQtnWpBPbQ8ZpptaZs14w6kE/MWfg7s= github.com/grafana/dataplane/sdata v0.0.9/go.mod h1:Jvs5ddpGmn6vcxT7tCTWAZ1mgi4sbcdFt9utQx5uMAU= -github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 h1:qcSGhr691f1mmPHwg2svGyO40Ex92G02aOyHzP6XHCE= -github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914/go.mod h1:OiN4P4aC6LwLzLbEupH3Ue83VfQoNMfG48rsna8jI/E= +github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 h1:jSojuc7njleS3UOz223WDlXOinmuLAIPI0z2vtq8EgI= +github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4/go.mod h1:VahT+GtfQIM+o8ht2StR6J9g+Ef+C2Vokh5uuSmOD/4= github.com/grafana/e2e v0.1.1 h1:/b6xcv5BtoBnx8cZnCiey9DbjEc8z7gXHO5edoeRYxc= github.com/grafana/e2e v0.1.1/go.mod h1:RpNLgae5VT+BUHvPE+/zSypmOXKwEu4t+tnEMS1ATaE= github.com/grafana/gofpdf v0.0.0-20250307124105-3b9c5d35577f h1:5xkjl5Y/j2QefJKOtTfyD1wXlVsQ2yEXmd0u82h5obs= @@ -3456,15 +3454,15 @@ 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-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY= -google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE= +google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 h1:d8Nakh1G+ur7+P3GcMjpRDEkoLUcLW2iU92XVqR+XMQ= +google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090/go.mod h1:U8EXRNSd8sUYyDfs/It7KVWodQr+Hf9xtxyxWudSwEw= 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= google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 h1:/OQuEa4YWtDt7uQWHd3q3sUMb+QOLQUg1xa8CEsRv5w= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= diff --git a/go.work.sum b/go.work.sum index 4eebbab7079..89d2523a51c 100644 --- a/go.work.sum +++ b/go.work.sum @@ -554,6 +554,7 @@ github.com/GoogleCloudPlatform/cloudsql-proxy v1.36.0 h1:kAtNAWwvTt5+iew6baV0kbO github.com/GoogleCloudPlatform/cloudsql-proxy v1.36.0/go.mod h1:VRKXU8C7Y/aUKjRBTGfw0Ndv4YqNxlB8zAPJJDxbASE= github.com/GoogleCloudPlatform/cloudsql-proxy v1.37.6 h1:UucmvNRPE75F3KzT68GHhKzOPwttxiFkh1d5LTTywW8= github.com/GoogleCloudPlatform/cloudsql-proxy v1.37.6/go.mod h1:XGripOBEUAcge8IUWR/NMAB5qO9k82tkbpoewBpyjYQ= +github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.2 h1:DBjmt6/otSdULyJdVg2BlG0qGZO5tKL4VzOs0jpvw5Q= github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.2/go.mod h1:dppbR7CwXD4pgtV9t3wD1812RaLDcBjtblcDF5f1vI0= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1/go.mod h1:jyqM3eLpJ3IbIFDTKVz2rF9T/xWGW0rIriGwnz8l9Tk= @@ -865,6 +866,7 @@ github.com/fluent/fluent-bit-go v0.0.0-20230731091245-a7a013e2473c h1:yKN46XJHYC github.com/fluent/fluent-bit-go v0.0.0-20230731091245-a7a013e2473c/go.mod h1:L92h+dgwElEyUuShEwjbiHjseW410WIcNz+Bjutc8YQ= github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fsouza/fake-gcs-server v1.7.0 h1:Un0BXUXrRWYSmYyC1Rqm2e2WJfTPyDy/HGMz31emTi8= github.com/fsouza/fake-gcs-server v1.52.2 h1:j6ne83nqHrlX5EEor7WWVIKdBsztGtwJ1J2mL+k+iio= @@ -928,12 +930,14 @@ github.com/gocraft/dbr/v2 v2.7.2/go.mod h1:5bCqyIXO5fYn3jEp/L06QF4K1siFdhxChMjdN github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c= github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/gomarkdown/markdown v0.0.0-20230716120725-531d2d74bc12 h1:uK3X/2mt4tbSGoHvbLBHUny7CKiuwUip3MArtukol4E= github.com/gomarkdown/markdown v0.0.0-20230716120725-531d2d74bc12/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo= github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= @@ -1058,20 +1062,31 @@ github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= github.com/hamba/avro/v2 v2.28.0 h1:E8J5D27biyAulWKNiEBhV85QPc9xRMCUCGJewS0KYCE= github.com/hamba/avro/v2 v2.28.0/go.mod h1:9TVrlt1cG1kkTUtm9u2eO5Qb7rZXlYzoKqPt8TSH+TA= +github.com/hashicorp/consul/api v1.15.3/go.mod h1:/g/qgcoBcEXALCNZgRRisyTW0nY86++L0KbeAMXYCeY= +github.com/hashicorp/consul/sdk v0.11.0/go.mod h1:yPkX5Q6CsxTFMjQQDJwzeNmUUF5NUGGbrDsv9wTb8cw= +github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-memdb v1.3.4 h1:XSL3NR682X/cVk2IeV0d70N4DZ9ljI885xAEU8IoK3c= github.com/hashicorp/go-memdb v1.3.4/go.mod h1:uBTr1oQbtuMgd1SSGoR8YV27eT3sBHbYiNm53bMpgSg= github.com/hashicorp/go-msgpack v1.1.5 h1:9byZdVjKTe5mce63pRVNP1L7UAmdHOTEMGehn6KvJWs= github.com/hashicorp/go-msgpack v1.1.5/go.mod h1:gWVc3sv/wbDmR3rQsj1CAktEZzoz1YNK9NfGLXJ69/4= github.com/hashicorp/go-msgpack/v2 v2.1.1/go.mod h1:upybraOAblm4S7rx0+jeNy+CWWhzywQsSRV5033mMu4= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +github.com/hashicorp/go-sockaddr v1.0.5/go.mod h1:uoUUmtwU7n9Dv3O4SNLeFvg0SxQ3lyjsj6+CCykpaxI= github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= +github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1 h1:sNCoNyDEvN1xa+X0baata4RdcpKwcMS6DH+xwfqPgjw= +github.com/hashicorp/golang-lru/v2 v2.0.5/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/mdns v1.0.5 h1:1M5hW1cunYeoXOqHwEb/GBDDHAFo0Yqb/uz/beC6LbE= github.com/hashicorp/mdns v1.0.5/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= +github.com/hashicorp/memberlist v0.3.1/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/raft v1.7.0 h1:4u24Qn6lQ6uwziM++UgsyiT64Q8GyRn43CV41qPiz1o= github.com/hashicorp/raft v1.7.0/go.mod h1:N1sKh6Vn47mrWvEArQgILTyng8GoDRNYlgKyK7PMjs0= github.com/hashicorp/raft-wal v0.4.1 h1:aU8XZ6x8R9BAIB/83Z1dTDtXvDVmv9YVYeXxd/1QBSA= github.com/hashicorp/raft-wal v0.4.1/go.mod h1:A6vP5o8hGOs1LHfC1Okh9xPwWDcmb6Vvuz/QyqUXlOE= +github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= github.com/heroku/x v0.0.61 h1:yfoAAtnFWSFZj+UlS+RZL/h8QYEp1R4wHVEg0G+Hwh4= github.com/heroku/x v0.0.61/go.mod h1:C7xYbpMdond+s6L5VpniDUSVPRwm3kZum1o7XiD5ZHk= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= @@ -1207,6 +1222,7 @@ github.com/mfridman/xflag v0.1.0/go.mod h1:/483ywM5ZO5SuMVjrIGquYNE5CzLrj5Ux/LxW github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg= github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU= github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/mitchellh/cli v1.1.5 h1:OxRIeJXpAMztws/XHlN2vu6imG5Dpq+j61AzAX5fLng= @@ -1215,8 +1231,10 @@ github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0 github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/gox v0.4.0 h1:lfGJxY7ToLJQjHHwi0EX6uYBdK78egf954SQl13PQJc= github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mithrandie/readline-csvq v1.3.0 h1:VTJEOGouJ8j27jJCD4kBBbNTxM0OdBvE1aY1tMhlqE8= github.com/mithrandie/readline-csvq v1.3.0/go.mod h1:FKyYqDgf/G4SNov7SMFXRWO6LQLXIOeTog/NB97FZl0= github.com/moby/moby v27.5.1+incompatible h1:/pN59F/t3U7Q4FPzV88nzqf7Fp0qqCSL2KzhZaiKcKw= @@ -1315,6 +1333,7 @@ github.com/open-telemetry/opentelemetry-collector-contrib/receiver/opencensusrec github.com/open-telemetry/opentelemetry-collector-contrib/receiver/opencensusreceiver v0.124.1/go.mod h1:4+9pSfniXXdRpkKf0QNdElOd7yIWD4ux8D260tSPV54= github.com/open-telemetry/opentelemetry-collector-contrib/receiver/zipkinreceiver v0.124.1 h1:XkxqUEoukMWXF+EpEWeM9itXKt62yKi13Lzd8ZEASP4= github.com/open-telemetry/opentelemetry-collector-contrib/receiver/zipkinreceiver v0.124.1/go.mod h1:CuCZVPz+yn88b5vhZPAlxaMrVuhAVexUV6f8b07lpUc= +github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e/go.mod h1:DYR5Eij8rJl8h7gblRrOZ8g0kW1umSpKqYIBTgeDtLo= 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= @@ -1357,6 +1376,7 @@ github.com/prometheus/common v0.64.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGI github.com/prometheus/common v0.66.0 h1:K/rJPHrG3+AoQs50r2+0t7zMnMzek2Vbv31OFVsMeVY= github.com/prometheus/common v0.66.0/go.mod h1:Ux6NtV1B4LatamKE63tJBntoxD++xmtI/lK0VtEplN4= github.com/prometheus/common/assets v0.2.0 h1:0P5OrzoHrYBOSM1OigWL3mY8ZvV2N4zIE/5AahrSrfM= +github.com/prometheus/exporter-toolkit v0.10.1-0.20230714054209-2f4150c63f97/go.mod h1:LoBCZeRh+5hX+fSULNyFnagYlQG/gBsyA/deNzROkq8= github.com/prometheus/statsd_exporter v0.26.1 h1:ucbIAdPmwAUcA+dU+Opok8Qt81Aw8HanlO+2N/Wjv7w= github.com/prometheus/statsd_exporter v0.26.1/go.mod h1:XlDdjAmRmx3JVvPPYuFNUg+Ynyb5kR69iPPkQjxXFMk= github.com/pterm/pterm v0.12.80 h1:mM55B+GnKUnLMUSqhdINe4s6tOuVQIetQ3my8JGyAIg= @@ -1381,6 +1401,7 @@ github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529/go.mod h1:qe5TWALJ8/a1 github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245 h1:K1Xf3bKttbF+koVGaX5xngRIZ5bVjbmPnaxE/dR08uY= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.2+incompatible h1:C89EOx/XBWwIXl8wm8OPJBd7kPF25UfsK2X7Ph/zCAk= github.com/sagikazarmark/crypt v0.6.0 h1:REOEXCs/NFY/1jOCEouMuT4zEniE5YoXbvpC5X/TLF8= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= @@ -1485,6 +1506,8 @@ github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg= github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= github.com/uber-go/atomic v1.4.0 h1:yOuPqEq4ovnhEjpHmfFwsqBXDYbQeT6Nb0bwD6XnD5o= github.com/uber-go/atomic v1.4.0/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= +github.com/uber/jaeger-client-go v2.28.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/vertica/vertica-sql-go v1.3.3 h1:fL+FKEAEy5ONmsvya2WH5T8bhkvY27y/Ik3ReR2T+Qw= @@ -1541,6 +1564,9 @@ gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b h1:7gd+rd8P3bqcn/9 go.einride.tech/aip v0.68.1 h1:16/AfSxcQISGN5z9C5lM+0mLYXihrHbQ1onvYTr93aQ= go.einride.tech/aip v0.68.1/go.mod h1:XaFtaj4HuA3Zwk9xoBtTWgNubZ0ZZXv9BZJCkuKuWbg= go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= go.etcd.io/gofail v0.2.0 h1:p19drv16FKK345a09a1iubchlw/vmRuksmRzgBIGjcA= go.etcd.io/gofail v0.2.0/go.mod h1:nL3ILMGfkXTekKI3clMBNazKnjUZjYLKmBHzsVAnC1o= go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= @@ -1695,6 +1721,7 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.4 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0/go.mod h1:HDBUsEjOuRC0EzKZ1bSaRGZWUBAzo+MhAcUUORSr4D0= 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/httptrace/otelhttptrace v0.60.0/go.mod h1:CosX/aS4eHnG9D7nESYpV753l4j9q5j3SL/PUYd2lR8= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= @@ -1707,6 +1734,7 @@ go.opentelemetry.io/contrib/otelconf v0.15.0 h1:BLNiIUsrNcqhSKpsa6CnhE6LdrpY1A8X go.opentelemetry.io/contrib/otelconf v0.15.0/go.mod h1:OPH1seO5z9dp1P26gnLtoM9ht7JDvh3Ws6XRHuXqImY= go.opentelemetry.io/contrib/propagators/b3 v1.35.0 h1:DpwKW04LkdFRFCIgM3sqwTJA/QREHMeMHYPWP1WeaPQ= go.opentelemetry.io/contrib/propagators/b3 v1.35.0/go.mod h1:9+SNxwqvCWo1qQwUpACBY5YKNVxFJn5mlbXg/4+uKBg= +go.opentelemetry.io/contrib/propagators/jaeger v1.35.0/go.mod h1:0ciyFyYZxE6JqRAQvIgGRabKWDUmNdW3GAQb6y/RlFU= go.opentelemetry.io/contrib/zpages v0.60.0 h1:wOM9ie1Hz4H88L9KE6GrGbKJhfm+8F1NfW/Y3q9Xt+8= go.opentelemetry.io/contrib/zpages v0.60.0/go.mod h1:xqfToSRGh2MYUsfyErNz8jnNDPlnpZqWM/y6Z2Cx7xw= go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= @@ -1725,6 +1753,7 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6o go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0/go.mod h1:90PoxvaEB5n6AOdZvi+yWJQoE95U8Dhhw2bSyRqnTD0= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0/go.mod h1:179AK5aar5R3eS9FucPy6rggvU0g52cvKId8pv4+v0c= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0/go.mod h1:r49hO7CgrxY9Voaj3Xe8pANWtr0Oq916d0XAmOoCZAQ= go.opentelemetry.io/otel/exporters/prometheus v0.58.0/go.mod h1:7qo/4CLI+zYSNbv0GMNquzuss2FVZo3OYrGh96n4HNc= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.35.0/go.mod h1:U2R3XyVPzn0WX7wOIypPuptulsMcPDPs/oiSVOMVnHY= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0/go.mod h1:PD57idA/AiFD5aqoxGxCvT/ILJPeHy3MjqU/NS7KogY= @@ -1755,6 +1784,7 @@ go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v8 go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY= go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= @@ -1771,10 +1801,12 @@ golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZv golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= +golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/exp v0.0.0-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-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= 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= @@ -1791,9 +1823,11 @@ golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/net v0.0.0-20190921015927-1a5e07d1ff72/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= @@ -1819,6 +1853,7 @@ golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= @@ -1883,6 +1918,7 @@ google.golang.org/api v0.229.0/go.mod h1:wyDfmq5g1wYJWn29O22FDWN48P7Xcz0xz+LBppt google.golang.org/api v0.232.0/go.mod h1:p9QCfBWZk1IJETUdbTKloR5ToFdKbYh2fkjsUL6vNoY= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc= google.golang.org/genproto v0.0.0-20250106144421-5f5ef82da422/go.mod h1:1NPAxoesyw/SgLPqaUp9u1f9PWCLAk/jVmhx7gJZStg= @@ -1910,14 +1946,18 @@ google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a/go. google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:kXqgZtrWaf6qS3jZOCnCH7WYfrvFjkC51bM8fz3RsCA= google.golang.org/genproto/googleapis/api v0.0.0-20250728155136-f173205681a0/go.mod h1:8ytArBbtOy2xfht+y2fqKd5DRDJRUQhqbyEnQ4bDChs= google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c/go.mod h1:ea2MjsO70ssTfCjiwHgI0ZFqcw45Ksuk2ckf9G468GA= +google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 h1:d8Nakh1G+ur7+P3GcMjpRDEkoLUcLW2iU92XVqR+XMQ= +google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090/go.mod h1:U8EXRNSd8sUYyDfs/It7KVWodQr+Hf9xtxyxWudSwEw= google.golang.org/genproto/googleapis/bytestream v0.0.0-20250505200425-f936aa4a68b2 h1:DbpkGFGRkd4GORg+IWQW2EhxUaa/My/PM8d1CGyTDMY= google.golang.org/genproto/googleapis/bytestream v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:h6yxum/C2qRb4txaZRLDHK8RyS0H/o2oEDeKY4onY/Y= google.golang.org/genproto/googleapis/bytestream v0.0.0-20250512202823-5a2f75b736a9 h1:YI36gCL8AQMhzYN6+jH8PdV/iZ0On+Zd0rO/7lCH3k8= google.golang.org/genproto/googleapis/bytestream v0.0.0-20250512202823-5a2f75b736a9/go.mod h1:h6yxum/C2qRb4txaZRLDHK8RyS0H/o2oEDeKY4onY/Y= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= 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-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-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-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA= @@ -1935,6 +1975,10 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go. google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/genproto/googleapis/rpc v0.0.0-20250728155136-f173205681a0/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 h1:/OQuEa4YWtDt7uQWHd3q3sUMb+QOLQUg1xa8CEsRv5w= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= @@ -1947,6 +1991,7 @@ google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= google.golang.org/grpc v1.74.0/go.mod h1:NZUaK8dAMUfzhK6uxZ+9511LtOrk73UGWOFoNvz7z+s= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE= google.golang.org/grpc/examples v0.0.0-20230224211313-3775f633ce20 h1:MLBCGN1O7GzIx+cBiwfYPwtmZ41U3Mn/cotLJciaArI= diff --git a/pkg/aggregator/go.mod b/pkg/aggregator/go.mod index 1c73fe6d54d..a60faec9045 100644 --- a/pkg/aggregator/go.mod +++ b/pkg/aggregator/go.mod @@ -152,8 +152,8 @@ require ( golang.org/x/tools v0.37.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect google.golang.org/grpc v1.75.0 // indirect google.golang.org/protobuf v1.36.8 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect diff --git a/pkg/aggregator/go.sum b/pkg/aggregator/go.sum index 5384797bc49..597985c485e 100644 --- a/pkg/aggregator/go.sum +++ b/pkg/aggregator/go.sum @@ -493,10 +493,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-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4= google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s= -google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY= -google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc= +google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 h1:d8Nakh1G+ur7+P3GcMjpRDEkoLUcLW2iU92XVqR+XMQ= +google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090/go.mod h1:U8EXRNSd8sUYyDfs/It7KVWodQr+Hf9xtxyxWudSwEw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 h1:/OQuEa4YWtDt7uQWHd3q3sUMb+QOLQUg1xa8CEsRv5w= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og= 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 5e11c96d5c3..3b4af4aa4f0 100644 --- a/pkg/apimachinery/go.mod +++ b/pkg/apimachinery/go.mod @@ -3,9 +3,8 @@ module github.com/grafana/grafana/pkg/apimachinery go 1.24.6 require ( - github.com/go-jose/go-jose/v3 v3.0.4 // @grafana/identity-access-team - github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43 // @grafana/identity-access-team - github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43 // @grafana/identity-access-team + github.com/grafana/authlib v0.0.0-20250909101823-1b466dbd19a1 // @grafana/identity-access-team + github.com/grafana/authlib/types v0.0.0-20250721184729-1593a38e4933 // @grafana/identity-access-team github.com/stretchr/testify v1.11.1 gopkg.in/yaml.v3 v3.0.1 k8s.io/apimachinery v0.33.3 @@ -14,6 +13,8 @@ require ( k8s.io/utils v0.0.0-20241210054802-24370beab758 ) +require github.com/go-jose/go-jose/v4 v4.1.2 + require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.12.1 // indirect @@ -25,7 +26,7 @@ require ( github.com/go-openapi/swag v0.23.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/google/gnostic-models v0.6.9 // indirect - github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 // indirect + github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/mailru/easyjson v0.9.0 // indirect @@ -40,7 +41,6 @@ require ( go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/otel v1.38.0 // indirect go.opentelemetry.io/otel/metric v1.38.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect go.opentelemetry.io/otel/trace v1.38.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect golang.org/x/crypto v0.42.0 // indirect @@ -48,7 +48,7 @@ require ( golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect golang.org/x/text v0.29.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect google.golang.org/grpc v1.75.0 // indirect google.golang.org/protobuf v1.36.8 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/pkg/apimachinery/go.sum b/pkg/apimachinery/go.sum index 3518a187dae..e5641c988e9 100644 --- a/pkg/apimachinery/go.sum +++ b/pkg/apimachinery/go.sum @@ -6,8 +6,8 @@ github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtz github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= -github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY= -github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= +github.com/go-jose/go-jose/v4 v4.1.2 h1:TK/7NqRQZfgAh+Td8AlsrvtPoUyiHh0LqVvokh+1vHI= +github.com/go-jose/go-jose/v4 v4.1.2/go.mod h1:22cg9HWM1pOlnRiY+9cQYJ9XHmya1bYW8OeDM6Ku6Oo= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -31,12 +31,12 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43 h1:vVPT0i5Y1vI6qzecYStV2yk7cHKrC3Pc7AgvwT5KydQ= -github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43/go.mod h1:1fWkOiL+m32NBgRHZtlZGz2ji868tPZACYbqP3nBRJI= -github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43 h1:NlkGMnVi/oUn6Cr90QbJYpQJ4FnjyAIG9Ex5GtTZIzw= -github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw= -github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 h1:qcSGhr691f1mmPHwg2svGyO40Ex92G02aOyHzP6XHCE= -github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914/go.mod h1:OiN4P4aC6LwLzLbEupH3Ue83VfQoNMfG48rsna8jI/E= +github.com/grafana/authlib v0.0.0-20250909101823-1b466dbd19a1 h1:qdH5s5FV+0Dyja8O1tBJq7MGd8nPCfxfsMimcYq5cRI= +github.com/grafana/authlib v0.0.0-20250909101823-1b466dbd19a1/go.mod h1:C6CmTG6vfiqebjJswKsc6zes+1F/OtTCi6aAtL5Um6A= +github.com/grafana/authlib/types v0.0.0-20250721184729-1593a38e4933 h1:GjiMR5NIO1/bYSCnt8x7VUeOMaupv2qXJkeLDVAddxQ= +github.com/grafana/authlib/types v0.0.0-20250721184729-1593a38e4933/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw= +github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 h1:jSojuc7njleS3UOz223WDlXOinmuLAIPI0z2vtq8EgI= +github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4/go.mod h1:VahT+GtfQIM+o8ht2StR6J9g+Ef+C2Vokh5uuSmOD/4= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -69,14 +69,12 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= @@ -94,70 +92,42 @@ go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= 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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -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.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 h1:/OQuEa4YWtDt7uQWHd3q3sUMb+QOLQUg1xa8CEsRv5w= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og= google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= @@ -167,7 +137,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/apimachinery v0.33.3 h1:4ZSrmNa0c/ZpZJhAgRdcsFcZOw1PQU1bALVQ0B3I5LA= diff --git a/pkg/apimachinery/identity/requester.go b/pkg/apimachinery/identity/requester.go index bd2ca2559ce..1c3b6cf8c9f 100644 --- a/pkg/apimachinery/identity/requester.go +++ b/pkg/apimachinery/identity/requester.go @@ -5,7 +5,8 @@ import ( "strconv" "time" - "github.com/go-jose/go-jose/v3/jwt" + jose "github.com/go-jose/go-jose/v4" + "github.com/go-jose/go-jose/v4/jwt" "k8s.io/apiserver/pkg/authentication/user" claims "github.com/grafana/authlib/types" @@ -136,7 +137,7 @@ func IsIDTokenExpired(requester Requester) bool { return false } - parsed, err := jwt.ParseSigned(idToken) + parsed, err := jwt.ParseSigned(idToken, []jose.SignatureAlgorithm{jose.ES256}) if err != nil { return false } diff --git a/pkg/apimachinery/identity/requester_test.go b/pkg/apimachinery/identity/requester_test.go index 9dbb027ad3d..030fc599dbc 100644 --- a/pkg/apimachinery/identity/requester_test.go +++ b/pkg/apimachinery/identity/requester_test.go @@ -1,11 +1,15 @@ package identity_test import ( + "crypto/ecdsa" + "crypto/x509" + "encoding/pem" + "fmt" "testing" "time" - "github.com/go-jose/go-jose/v3" - "github.com/go-jose/go-jose/v3/jwt" + "github.com/go-jose/go-jose/v4" + "github.com/go-jose/go-jose/v4/jwt" "github.com/stretchr/testify/require" "github.com/grafana/grafana/pkg/apimachinery/identity" @@ -75,9 +79,33 @@ func TestIsIDTokenExpired(t *testing.T) { } } +var testKey = decodePrivateKey([]byte(` +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEID6lXWsmcv/UWn9SptjOThsy88cifgGIBj2Lu0M9I8tQoAoGCCqGSM49 +AwEHoUQDQgAEsf6eNnNMNhl+q7jXsbdUf3ADPh248uoFUSSV9oBzgptyokHCjJz6 +n6PKDm2W7i3S2+dAs5M5f3s7d8KiLjGZdQ== +-----END EC PRIVATE KEY----- +`)) + +func decodePrivateKey(data []byte) *ecdsa.PrivateKey { + block, _ := pem.Decode(data) + if block == nil { + panic("should include PEM block") + } + + privateKey, err := x509.ParseECPrivateKey(block.Bytes) + if err != nil { + panic(fmt.Sprintf("should be able to parse ec private key: %v", err)) + } + if privateKey.Curve.Params().Name != "P-256" { + panic("should be valid private key") + } + + return privateKey +} + func createToken(t *testing.T, exp *time.Time) string { - key := []byte("test-secret-key") - signer, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: key}, nil) + signer, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.ES256, Key: testKey}, nil) require.NoError(t, err) claims := struct { @@ -92,7 +120,7 @@ func createToken(t *testing.T, exp *time.Time) string { claims.Expiry = jwt.NewNumericDate(*exp) } - token, err := jwt.Signed(signer).Claims(claims).CompactSerialize() + token, err := jwt.Signed(signer).Claims(claims).Serialize() require.NoError(t, err) return token } diff --git a/pkg/apiserver/go.mod b/pkg/apiserver/go.mod index 93b0d96d8a4..e9963061ad5 100644 --- a/pkg/apiserver/go.mod +++ b/pkg/apiserver/go.mod @@ -4,7 +4,7 @@ go 1.24.6 require ( github.com/google/go-cmp v0.7.0 - github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43 + github.com/grafana/authlib/types v0.0.0-20250721184729-1593a38e4933 github.com/grafana/grafana-app-sdk/logging v0.40.3 github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e github.com/prometheus/client_golang v1.23.1 @@ -31,7 +31,7 @@ require ( github.com/emicklei/go-restful/v3 v3.12.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect - github.com/go-jose/go-jose/v3 v3.0.4 // indirect + github.com/go-jose/go-jose/v4 v4.1.2 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect @@ -43,8 +43,8 @@ require ( github.com/google/gnostic-models v0.6.9 // indirect github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43 // indirect - github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 // indirect + github.com/grafana/authlib v0.0.0-20250909101823-1b466dbd19a1 // indirect + github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 // indirect github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 // indirect @@ -93,8 +93,8 @@ require ( golang.org/x/time v0.13.0 // indirect golang.org/x/tools v0.37.0 // indirect google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect google.golang.org/grpc v1.75.0 // indirect google.golang.org/protobuf v1.36.8 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect diff --git a/pkg/apiserver/go.sum b/pkg/apiserver/go.sum index 10213c03aac..c8d254d4ed6 100644 --- a/pkg/apiserver/go.sum +++ b/pkg/apiserver/go.sum @@ -33,8 +33,8 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= -github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY= -github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= +github.com/go-jose/go-jose/v4 v4.1.2 h1:TK/7NqRQZfgAh+Td8AlsrvtPoUyiHh0LqVvokh+1vHI= +github.com/go-jose/go-jose/v4 v4.1.2/go.mod h1:22cg9HWM1pOlnRiY+9cQYJ9XHmya1bYW8OeDM6Ku6Oo= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -78,12 +78,12 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= -github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43 h1:vVPT0i5Y1vI6qzecYStV2yk7cHKrC3Pc7AgvwT5KydQ= -github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43/go.mod h1:1fWkOiL+m32NBgRHZtlZGz2ji868tPZACYbqP3nBRJI= -github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43 h1:NlkGMnVi/oUn6Cr90QbJYpQJ4FnjyAIG9Ex5GtTZIzw= -github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw= -github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 h1:qcSGhr691f1mmPHwg2svGyO40Ex92G02aOyHzP6XHCE= -github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914/go.mod h1:OiN4P4aC6LwLzLbEupH3Ue83VfQoNMfG48rsna8jI/E= +github.com/grafana/authlib v0.0.0-20250909101823-1b466dbd19a1 h1:qdH5s5FV+0Dyja8O1tBJq7MGd8nPCfxfsMimcYq5cRI= +github.com/grafana/authlib v0.0.0-20250909101823-1b466dbd19a1/go.mod h1:C6CmTG6vfiqebjJswKsc6zes+1F/OtTCi6aAtL5Um6A= +github.com/grafana/authlib/types v0.0.0-20250721184729-1593a38e4933 h1:GjiMR5NIO1/bYSCnt8x7VUeOMaupv2qXJkeLDVAddxQ= +github.com/grafana/authlib/types v0.0.0-20250721184729-1593a38e4933/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw= +github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 h1:jSojuc7njleS3UOz223WDlXOinmuLAIPI0z2vtq8EgI= +github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4/go.mod h1:VahT+GtfQIM+o8ht2StR6J9g+Ef+C2Vokh5uuSmOD/4= github.com/grafana/grafana-app-sdk/logging v0.40.3 h1:2VXsXXEQiqAavRP8wusRDB6rDqf5lufP7A6NfjELqPE= github.com/grafana/grafana-app-sdk/logging v0.40.3/go.mod h1:otUD9XpJD7A5sCLb8mcs9hIXGdeV6lnhzVwe747g4RU= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e h1:BTKk7LHuG1kmAkucwTA7DuMbKpKvJTKrGdBmUNO4dfQ= @@ -182,7 +182,6 @@ github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 h1:S2dVYn90KE98chq github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk= go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= go.etcd.io/etcd/api/v3 v3.5.21 h1:A6O2/JDb3tvHhiIz3xf9nJ7REHvtEFJJ3veW3FbCnS8= @@ -240,8 +239,6 @@ go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -251,8 +248,6 @@ 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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 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= @@ -262,10 +257,6 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -276,8 +267,6 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -285,29 +274,13 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -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.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ= golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI= @@ -322,8 +295,6 @@ 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.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -339,10 +310,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-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4= google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s= -google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY= -google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc= +google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 h1:d8Nakh1G+ur7+P3GcMjpRDEkoLUcLW2iU92XVqR+XMQ= +google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090/go.mod h1:U8EXRNSd8sUYyDfs/It7KVWodQr+Hf9xtxyxWudSwEw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 h1:/OQuEa4YWtDt7uQWHd3q3sUMb+QOLQUg1xa8CEsRv5w= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og= 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/build/go.mod b/pkg/build/go.mod index 4e508a233a2..f564354892d 100644 --- a/pkg/build/go.mod +++ b/pkg/build/go.mod @@ -30,8 +30,8 @@ require ( github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect go.opentelemetry.io/otel/metric v1.38.0 // indirect golang.org/x/sys v0.36.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect ) require ( diff --git a/pkg/build/go.sum b/pkg/build/go.sum index 444f9bdd5a4..42fcce66d28 100644 --- a/pkg/build/go.sum +++ b/pkg/build/go.sum @@ -105,10 +105,10 @@ golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY= -google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc= +google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 h1:d8Nakh1G+ur7+P3GcMjpRDEkoLUcLW2iU92XVqR+XMQ= +google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090/go.mod h1:U8EXRNSd8sUYyDfs/It7KVWodQr+Hf9xtxyxWudSwEw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 h1:/OQuEa4YWtDt7uQWHd3q3sUMb+QOLQUg1xa8CEsRv5w= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og= google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= diff --git a/pkg/extensions/enterprise_imports.go b/pkg/extensions/enterprise_imports.go index f464d6543db..c50d72f77c8 100644 --- a/pkg/extensions/enterprise_imports.go +++ b/pkg/extensions/enterprise_imports.go @@ -15,7 +15,7 @@ import ( _ "github.com/blugelabs/bluge" _ "github.com/blugelabs/bluge_segment_api" _ "github.com/crewjam/saml" - _ "github.com/go-jose/go-jose/v3" + _ "github.com/go-jose/go-jose/v4" _ "github.com/gobwas/glob" _ "github.com/googleapis/gax-go/v2" _ "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus" @@ -53,6 +53,7 @@ import ( _ "github.com/grafana/e2e" _ "github.com/grafana/gofpdf" _ "github.com/grafana/gomemcache/memcache" - _ "github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1" _ "github.com/grafana/tempo/pkg/traceql" + + _ "github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1" ) diff --git a/pkg/login/social/connectors/azuread_oauth.go b/pkg/login/social/connectors/azuread_oauth.go index 27660e61f77..8bde47f2aa4 100644 --- a/pkg/login/social/connectors/azuread_oauth.go +++ b/pkg/login/social/connectors/azuread_oauth.go @@ -12,8 +12,8 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" "github.com/Azure/azure-sdk-for-go/sdk/azidentity" - jose "github.com/go-jose/go-jose/v3" - "github.com/go-jose/go-jose/v3/jwt" + jose "github.com/go-jose/go-jose/v4" + "github.com/go-jose/go-jose/v4/jwt" "github.com/google/uuid" "golang.org/x/oauth2" @@ -124,7 +124,7 @@ func (s *SocialAzureAD) UserInfo(ctx context.Context, client *http.Client, token return nil, ErrIDTokenNotFound } - parsedToken, err := jwt.ParseSigned(idToken.(string)) + parsedToken, err := jwt.ParseSigned(idToken.(string), []jose.SignatureAlgorithm{jose.PS256, jose.RS256, jose.RS512, jose.ES256}) if err != nil { return nil, fmt.Errorf("error parsing id token: %w", err) } @@ -548,7 +548,7 @@ func (s *SocialAzureAD) groupsGraphAPIURL(claims *azureClaims, token *oauth2.Tok tenantID := claims.TenantID // If tenantID wasn't found in the id_token, parse access token if tenantID == "" { - parsedToken, err := jwt.ParseSigned(token.AccessToken) + parsedToken, err := jwt.ParseSigned(token.AccessToken, []jose.SignatureAlgorithm{jose.PS256, jose.RS256, jose.RS512, jose.ES256}) if err != nil { return "", fmt.Errorf("error parsing access token: %w", err) } diff --git a/pkg/login/social/connectors/azuread_oauth_test.go b/pkg/login/social/connectors/azuread_oauth_test.go index f8c8bde01a6..96b6d878910 100644 --- a/pkg/login/social/connectors/azuread_oauth_test.go +++ b/pkg/login/social/connectors/azuread_oauth_test.go @@ -11,8 +11,8 @@ import ( "testing" "time" - "github.com/go-jose/go-jose/v3" - "github.com/go-jose/go-jose/v3/jwt" + "github.com/go-jose/go-jose/v4" + "github.com/go-jose/go-jose/v4/jwt" "github.com/stretchr/testify/require" "golang.org/x/oauth2" @@ -880,10 +880,10 @@ func TestSocialAzureAD_UserInfo(t *testing.T) { tt.claims.ClaimNames.Groups: {Endpoint: server.URL}, } } - raw, err = jwt.Signed(sig).Claims(cl).Claims(tt.claims).CompactSerialize() + raw, err = jwt.Signed(sig).Claims(cl).Claims(tt.claims).Serialize() require.NoError(t, err) } else { - raw, err = jwt.Signed(sig).Claims(cl).CompactSerialize() + raw, err = jwt.Signed(sig).Claims(cl).Serialize() require.NoError(t, err) } @@ -1054,10 +1054,10 @@ func TestSocialAzureAD_SkipOrgRole(t *testing.T) { tt.claims.ClaimNames.Groups: {Endpoint: server.URL}, } } - raw, err = jwt.Signed(sig).Claims(cl).Claims(tt.claims).CompactSerialize() + raw, err = jwt.Signed(sig).Claims(cl).Claims(tt.claims).Serialize() require.NoError(t, err) } else { - raw, err = jwt.Signed(sig).Claims(cl).CompactSerialize() + raw, err = jwt.Signed(sig).Claims(cl).Serialize() require.NoError(t, err) } diff --git a/pkg/login/social/connectors/google_oauth_test.go b/pkg/login/social/connectors/google_oauth_test.go index 861167df9e4..d330c39d78d 100644 --- a/pkg/login/social/connectors/google_oauth_test.go +++ b/pkg/login/social/connectors/google_oauth_test.go @@ -2,14 +2,18 @@ package connectors import ( "context" + "crypto/ecdsa" + "crypto/x509" + "encoding/pem" "errors" + "fmt" "net/http" "net/http/httptest" "testing" "time" - "github.com/go-jose/go-jose/v3" - "github.com/go-jose/go-jose/v3/jwt" + "github.com/go-jose/go-jose/v4" + "github.com/go-jose/go-jose/v4/jwt" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/oauth2" @@ -240,6 +244,31 @@ const googleGroupsJSON = ` } ` +var testKey = decodePrivateKey([]byte(` +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEID6lXWsmcv/UWn9SptjOThsy88cifgGIBj2Lu0M9I8tQoAoGCCqGSM49 +AwEHoUQDQgAEsf6eNnNMNhl+q7jXsbdUf3ADPh248uoFUSSV9oBzgptyokHCjJz6 +n6PKDm2W7i3S2+dAs5M5f3s7d8KiLjGZdQ== +-----END EC PRIVATE KEY----- +`)) + +func decodePrivateKey(data []byte) *ecdsa.PrivateKey { + block, _ := pem.Decode(data) + if block == nil { + panic("should include PEM block") + } + + privateKey, err := x509.ParseECPrivateKey(block.Bytes) + if err != nil { + panic(fmt.Sprintf("should be able to parse ec private key: %v", err)) + } + if privateKey.Curve.Params().Name != "P-256" { + panic("should be valid private key") + } + + return privateKey +} + func TestSocialGoogle_UserInfo(t *testing.T) { cl := jwt.Claims{ Subject: "88888888888888", @@ -248,7 +277,7 @@ func TestSocialGoogle_UserInfo(t *testing.T) { Audience: jwt.Audience{"823123"}, } - sig, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: []byte("secret")}, (&jose.SignerOptions{}).WithType("JWT")) + sig, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.ES256, Key: testKey}, (&jose.SignerOptions{}).WithType("JWT")) require.NoError(t, err) idMap := map[string]any{ "email": "test@example.com", @@ -257,7 +286,7 @@ func TestSocialGoogle_UserInfo(t *testing.T) { "email_verified": true, } - raw, err := jwt.Signed(sig).Claims(cl).Claims(idMap).CompactSerialize() + raw, err := jwt.Signed(sig).Claims(cl).Claims(idMap).Serialize() require.NoError(t, err) tokenWithID := (&oauth2.Token{ diff --git a/pkg/login/social/connectors/okta_oauth.go b/pkg/login/social/connectors/okta_oauth.go index 597051f7eae..b91a73c92cb 100644 --- a/pkg/login/social/connectors/okta_oauth.go +++ b/pkg/login/social/connectors/okta_oauth.go @@ -7,7 +7,8 @@ import ( "fmt" "net/http" - "github.com/go-jose/go-jose/v3/jwt" + jose "github.com/go-jose/go-jose/v4" + "github.com/go-jose/go-jose/v4/jwt" "golang.org/x/oauth2" "github.com/grafana/grafana/pkg/apimachinery/identity" @@ -114,8 +115,8 @@ func (s *SocialOkta) UserInfo(ctx context.Context, client *http.Client, token *o if idToken == nil { return nil, fmt.Errorf("no id_token found") } - - parsedToken, err := jwt.ParseSigned(idToken.(string)) + parsedToken, err := jwt.ParseSigned(idToken.(string), []jose.SignatureAlgorithm{jose.HS256, + jose.HS384, jose.HS512, jose.RS256, jose.RS384, jose.RS512, jose.ES256, jose.ES384, jose.ES512}) if err != nil { return nil, fmt.Errorf("error parsing id token: %w", err) } diff --git a/pkg/promlib/go.mod b/pkg/promlib/go.mod index 6cd2f65a6fa..58d8c5f515e 100644 --- a/pkg/promlib/go.mod +++ b/pkg/promlib/go.mod @@ -3,7 +3,7 @@ module github.com/grafana/grafana/pkg/promlib go 1.24.6 require ( - github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 + github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 github.com/grafana/grafana-plugin-sdk-go v0.278.0 github.com/json-iterator/go v1.1.12 github.com/prometheus/client_golang v1.23.1 @@ -127,8 +127,8 @@ require ( golang.org/x/tools v0.37.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect google.golang.org/api v0.235.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect google.golang.org/grpc v1.75.0 // indirect gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/pkg/promlib/go.sum b/pkg/promlib/go.sum index 27480578065..b193d5761ee 100644 --- a/pkg/promlib/go.sum +++ b/pkg/promlib/go.sum @@ -133,8 +133,8 @@ github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25d github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 h1:qcSGhr691f1mmPHwg2svGyO40Ex92G02aOyHzP6XHCE= -github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914/go.mod h1:OiN4P4aC6LwLzLbEupH3Ue83VfQoNMfG48rsna8jI/E= +github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 h1:jSojuc7njleS3UOz223WDlXOinmuLAIPI0z2vtq8EgI= +github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4/go.mod h1:VahT+GtfQIM+o8ht2StR6J9g+Ef+C2Vokh5uuSmOD/4= github.com/grafana/grafana-plugin-sdk-go v0.278.0 h1:5/rIYparLi02pofdaag8wnjspMMVNCi8cZhC4cdC3Ho= github.com/grafana/grafana-plugin-sdk-go v0.278.0/go.mod h1:+8NXT/XUJ/89GV6FxGQ366NZ3nU+cAXDMd0OUESF9H4= github.com/grafana/otel-profiling-go v0.5.1 h1:stVPKAFZSa7eGiqbYuG25VcqYksR6iWvF3YH66t4qL8= @@ -411,10 +411,10 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= google.golang.org/api v0.235.0 h1:C3MkpQSRxS1Jy6AkzTGKKrpSCOd2WOGrezZ+icKSkKo= google.golang.org/api v0.235.0/go.mod h1:QpeJkemzkFKe5VCE/PMv7GsUfn9ZF+u+q1Q7w6ckxTg= -google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY= -google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc= +google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 h1:d8Nakh1G+ur7+P3GcMjpRDEkoLUcLW2iU92XVqR+XMQ= +google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090/go.mod h1:U8EXRNSd8sUYyDfs/It7KVWodQr+Hf9xtxyxWudSwEw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 h1:/OQuEa4YWtDt7uQWHd3q3sUMb+QOLQUg1xa8CEsRv5w= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og= google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= diff --git a/pkg/registry/apis/folders/authorizer_test.go b/pkg/registry/apis/folders/authorizer_test.go index 290f6e44252..f8ccef456dd 100644 --- a/pkg/registry/apis/folders/authorizer_test.go +++ b/pkg/registry/apis/folders/authorizer_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/go-jose/go-jose/v3/jwt" + "github.com/go-jose/go-jose/v4/jwt" "github.com/stretchr/testify/require" "k8s.io/apiserver/pkg/authorization/authorizer" diff --git a/pkg/services/auth/idimpl/service.go b/pkg/services/auth/idimpl/service.go index cb8e6adc9ce..8060f66ed89 100644 --- a/pkg/services/auth/idimpl/service.go +++ b/pkg/services/auth/idimpl/service.go @@ -6,7 +6,8 @@ import ( "fmt" "time" - "github.com/go-jose/go-jose/v3/jwt" + jose "github.com/go-jose/go-jose/v4" + "github.com/go-jose/go-jose/v4/jwt" "github.com/prometheus/client_golang/prometheus" "go.opentelemetry.io/otel/trace" "golang.org/x/sync/singleflight" @@ -170,7 +171,7 @@ func (s *Service) SyncIDToken(ctx context.Context, identity *authn.Identity, _ * } func (s *Service) extractTokenClaims(token string) (*authnlib.Claims[authnlib.IDTokenClaims], error) { - parsed, err := jwt.ParseSigned(token) + parsed, err := jwt.ParseSigned(token, []jose.SignatureAlgorithm{jose.ES256}) if err != nil { s.metrics.failedTokenSigningCounter.Inc() return nil, err diff --git a/pkg/services/auth/idimpl/service_test.go b/pkg/services/auth/idimpl/service_test.go index 8690c5c24bc..a287fd427c8 100644 --- a/pkg/services/auth/idimpl/service_test.go +++ b/pkg/services/auth/idimpl/service_test.go @@ -2,14 +2,19 @@ package idimpl import ( "context" + "crypto/ecdsa" + "crypto/x509" + "encoding/pem" + "fmt" "testing" - "github.com/go-jose/go-jose/v3" - "github.com/go-jose/go-jose/v3/jwt" + "github.com/go-jose/go-jose/v4" + "github.com/go-jose/go-jose/v4/jwt" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" claims "github.com/grafana/authlib/types" + "github.com/grafana/grafana/pkg/infra/remotecache" "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/services/auth" @@ -35,14 +40,38 @@ func Test_ProvideService(t *testing.T) { }) } +var testKey = decodePrivateKey([]byte(` +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEID6lXWsmcv/UWn9SptjOThsy88cifgGIBj2Lu0M9I8tQoAoGCCqGSM49 +AwEHoUQDQgAEsf6eNnNMNhl+q7jXsbdUf3ADPh248uoFUSSV9oBzgptyokHCjJz6 +n6PKDm2W7i3S2+dAs5M5f3s7d8KiLjGZdQ== +-----END EC PRIVATE KEY----- +`)) + +func decodePrivateKey(data []byte) *ecdsa.PrivateKey { + block, _ := pem.Decode(data) + if block == nil { + panic("should include PEM block") + } + + privateKey, err := x509.ParseECPrivateKey(block.Bytes) + if err != nil { + panic(fmt.Sprintf("should be able to parse ec private key: %v", err)) + } + if privateKey.Curve.Params().Name != "P-256" { + panic("should be valid private key") + } + + return privateKey +} + func TestService_SignIdentity(t *testing.T) { signer := &idtest.FakeSigner{ SignIDTokenFn: func(_ context.Context, claims *auth.IDClaims) (string, error) { - key := []byte("key") - s, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: key}, nil) + s, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.ES256, Key: testKey}, nil) require.NoError(t, err) - token, err := jwt.Signed(s).Claims(claims.Claims).Claims(claims.Rest).CompactSerialize() + token, err := jwt.Signed(s).Claims(claims.Claims).Claims(claims.Rest).Serialize() require.NoError(t, err) return token, nil @@ -73,7 +102,7 @@ func TestService_SignIdentity(t *testing.T) { }) require.NoError(t, err) - parsed, err := jwt.ParseSigned(token) + parsed, err := jwt.ParseSigned(token, []jose.SignatureAlgorithm{jose.ES256}) require.NoError(t, err) gotClaims := &auth.IDClaims{} diff --git a/pkg/services/auth/idimpl/signer.go b/pkg/services/auth/idimpl/signer.go index 057421da57e..1bd9dbf79b7 100644 --- a/pkg/services/auth/idimpl/signer.go +++ b/pkg/services/auth/idimpl/signer.go @@ -3,8 +3,8 @@ package idimpl import ( "context" - "github.com/go-jose/go-jose/v3" - "github.com/go-jose/go-jose/v3/jwt" + "github.com/go-jose/go-jose/v4" + "github.com/go-jose/go-jose/v4/jwt" "github.com/grafana/grafana/pkg/services/auth" "github.com/grafana/grafana/pkg/services/signingkeys" @@ -33,7 +33,7 @@ func (s *LocalSigner) SignIDToken(ctx context.Context, claims *auth.IDClaims) (s builder := jwt.Signed(signer).Claims(&claims.Rest).Claims(claims.Claims) - token, err := builder.CompactSerialize() + token, err := builder.Serialize() if err != nil { return "", err } diff --git a/pkg/services/auth/jwt/auth.go b/pkg/services/auth/jwt/auth.go index aaa15b98321..7308d0acc02 100644 --- a/pkg/services/auth/jwt/auth.go +++ b/pkg/services/auth/jwt/auth.go @@ -6,7 +6,8 @@ import ( "errors" "strings" - "github.com/go-jose/go-jose/v3/jwt" + jose "github.com/go-jose/go-jose/v4" + "github.com/go-jose/go-jose/v4/jwt" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/remotecache" @@ -69,7 +70,8 @@ func (s *AuthService) Verify(ctx context.Context, strToken string) (map[string]a s.log.Debug("Parsing JSON Web Token") strToken = sanitizeJWT(strToken) - token, err := jwt.ParseSigned(strToken) + token, err := jwt.ParseSigned(strToken, []jose.SignatureAlgorithm{jose.EdDSA, jose.HS256, jose.HS384, + jose.HS512, jose.RS512, jose.RS256, jose.ES256, jose.ES384, jose.ES512, jose.PS256, jose.PS384, jose.PS512}) if err != nil { return nil, err } @@ -106,7 +108,8 @@ func (s *AuthService) Verify(ctx context.Context, strToken string) (map[string]a // HasSubClaim checks if the provided JWT token contains a non-empty "sub" claim. // Returns true if it contains, otherwise returns false. func HasSubClaim(jwtToken string) bool { - parsed, err := jwt.ParseSigned(sanitizeJWT(jwtToken)) + parsed, err := jwt.ParseSigned(sanitizeJWT(jwtToken), []jose.SignatureAlgorithm{jose.EdDSA, jose.HS256, jose.HS384, + jose.HS512, jose.RS512, jose.RS256, jose.ES256, jose.ES384, jose.ES512, jose.PS256, jose.PS384, jose.PS512}) if err != nil { return false } diff --git a/pkg/services/auth/jwt/auth_test.go b/pkg/services/auth/jwt/auth_test.go index 97d7c9dfa91..8528a04ab52 100644 --- a/pkg/services/auth/jwt/auth_test.go +++ b/pkg/services/auth/jwt/auth_test.go @@ -13,8 +13,8 @@ import ( "testing" "time" - jose "github.com/go-jose/go-jose/v3" - "github.com/go-jose/go-jose/v3/jwt" + jose "github.com/go-jose/go-jose/v4" + "github.com/go-jose/go-jose/v4/jwt" "github.com/madflojo/testcerts" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -454,10 +454,10 @@ func TestIntegrationClaimValidation(t *testing.T) { require.NoError(t, err) _, err = sc.authJWTSvc.Verify(sc.ctx, sign(t, key, jwt.Claims{Audience: []string{"foo"}}, nil)) - require.Error(t, err) + require.NoError(t, err) _, err = sc.authJWTSvc.Verify(sc.ctx, sign(t, key, jwt.Claims{Audience: []string{"bar", "baz"}}, nil)) - require.Error(t, err) + require.NoError(t, err) _, err = sc.authJWTSvc.Verify(sc.ctx, sign(t, key, jwt.Claims{Audience: []string{"baz"}}, nil)) require.Error(t, err) diff --git a/pkg/services/auth/jwt/key_sets.go b/pkg/services/auth/jwt/key_sets.go index b569546b6b1..dd1d05e1ed0 100644 --- a/pkg/services/auth/jwt/key_sets.go +++ b/pkg/services/auth/jwt/key_sets.go @@ -18,7 +18,7 @@ import ( "strings" "time" - jose "github.com/go-jose/go-jose/v3" + jose "github.com/go-jose/go-jose/v4" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/remotecache" diff --git a/pkg/services/auth/jwt/rsa_keys_test.go b/pkg/services/auth/jwt/rsa_keys_test.go index 5d5ede0ec55..5873c2c6a25 100644 --- a/pkg/services/auth/jwt/rsa_keys_test.go +++ b/pkg/services/auth/jwt/rsa_keys_test.go @@ -6,7 +6,7 @@ import ( "encoding/pem" "fmt" - "github.com/go-jose/go-jose/v3" + "github.com/go-jose/go-jose/v4" ) var rsaKeys [3]*rsa.PrivateKey diff --git a/pkg/services/auth/jwt/signing_test.go b/pkg/services/auth/jwt/signing_test.go index 91b34a910e5..2ad15caf87c 100644 --- a/pkg/services/auth/jwt/signing_test.go +++ b/pkg/services/auth/jwt/signing_test.go @@ -3,8 +3,8 @@ package jwt import ( "testing" - jose "github.com/go-jose/go-jose/v3" - "github.com/go-jose/go-jose/v3/jwt" + jose "github.com/go-jose/go-jose/v4" + "github.com/go-jose/go-jose/v4/jwt" "github.com/stretchr/testify/require" ) @@ -16,9 +16,9 @@ func sign(t *testing.T, key any, claims any, opts *jose.SignerOptions) string { if opts == nil { opts = &jose.SignerOptions{} } - sig, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.PS512, Key: key}, (opts).WithType("JWT")) + sig, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.RS256, Key: key}, (opts).WithType("JWT")) require.NoError(t, err) - token, err := jwt.Signed(sig).Claims(claims).CompactSerialize() + token, err := jwt.Signed(sig).Claims(claims).Serialize() require.NoError(t, err) return token } @@ -40,7 +40,7 @@ func signNone(t *testing.T, claims any) string { sig, err := jose.NewSigner(jose.SigningKey{Algorithm: "none", Key: noneSigner{}}, (&jose.SignerOptions{}).WithType("JWT")) require.NoError(t, err) - token, err := jwt.Signed(sig).Claims(claims).CompactSerialize() + token, err := jwt.Signed(sig).Claims(claims).Serialize() require.NoError(t, err) return token } diff --git a/pkg/services/auth/jwt/validation.go b/pkg/services/auth/jwt/validation.go index d6c347de01e..c5e8fbd4daa 100644 --- a/pkg/services/auth/jwt/validation.go +++ b/pkg/services/auth/jwt/validation.go @@ -6,7 +6,7 @@ import ( "reflect" "time" - "github.com/go-jose/go-jose/v3/jwt" + "github.com/go-jose/go-jose/v4/jwt" ) func (s *AuthService) initClaimExpectations() error { @@ -35,13 +35,13 @@ func (s *AuthService) initClaimExpectations() error { case []any: for _, val := range value { if v, ok := val.(string); ok { - s.expectRegistered.Audience = append(s.expectRegistered.Audience, v) + s.expectRegistered.AnyAudience = append(s.expectRegistered.AnyAudience, v) } else { return fmt.Errorf("%q expectation contains value with invalid type %T, string expected", key, val) } } case string: - s.expectRegistered.Audience = []string{value} + s.expectRegistered.AnyAudience = []string{value} default: return fmt.Errorf("%q expectation has invalid type %T, array or string expected", key, value) } diff --git a/pkg/services/authn/clients/ext_jwt.go b/pkg/services/authn/clients/ext_jwt.go index 5baafdf0c6b..b479f314c76 100644 --- a/pkg/services/authn/clients/ext_jwt.go +++ b/pkg/services/authn/clients/ext_jwt.go @@ -6,7 +6,8 @@ import ( "net/http" "strings" - "github.com/go-jose/go-jose/v3/jwt" + jose "github.com/go-jose/go-jose/v4" + "github.com/go-jose/go-jose/v4/jwt" "go.opentelemetry.io/otel/trace" authlib "github.com/grafana/authlib/authn" @@ -215,7 +216,7 @@ func (s *ExtendedJWT) Test(ctx context.Context, r *authn.Request) bool { return false } - parsedToken, err := jwt.ParseSigned(rawToken) + parsedToken, err := jwt.ParseSigned(rawToken, []jose.SignatureAlgorithm{jose.ES256}) if err != nil { return false } diff --git a/pkg/services/authn/clients/ext_jwt_test.go b/pkg/services/authn/clients/ext_jwt_test.go index b72b6303154..48612981a45 100644 --- a/pkg/services/authn/clients/ext_jwt_test.go +++ b/pkg/services/authn/clients/ext_jwt_test.go @@ -2,15 +2,16 @@ package clients import ( "context" + "crypto/ecdsa" + "crypto/elliptic" "crypto/rand" - "crypto/rsa" "fmt" "net/http" "testing" "time" - "github.com/go-jose/go-jose/v3" - "github.com/go-jose/go-jose/v3/jwt" + "github.com/go-jose/go-jose/v4" + "github.com/go-jose/go-jose/v4/jwt" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -127,7 +128,8 @@ var ( }, } - pk, _ = rsa.GenerateKey(rand.Reader, 4096) + // generate ES256 key + pk, _ = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) ) var _ authnlib.Verifier[authnlib.IDTokenClaims] = &mockIDVerifier{} @@ -173,12 +175,12 @@ func TestExtendedJWT_Test(t *testing.T) { }, { name: "should return true when Authorization header contains Bearer prefix", - authHeaderFunc: func() string { return "Bearer " + generateToken(validAccessTokenClaims, pk, jose.RS256) }, + authHeaderFunc: func() string { return "Bearer " + generateToken(t, validAccessTokenClaims, pk, jose.ES256) }, want: true, }, { name: "should return true when Authorization header only contains the token", - authHeaderFunc: func() string { return generateToken(validAccessTokenClaims, pk, jose.RS256) }, + authHeaderFunc: func() string { return generateToken(t, validAccessTokenClaims, pk, jose.ES256) }, want: true, }, { @@ -485,7 +487,7 @@ func TestExtendedJWT_Authenticate(t *testing.T) { validHTTPReq := &http.Request{ Header: map[string][]string{ - "X-Access-Token": {generateToken(*tc.accessToken, pk, jose.RS256)}, + "X-Access-Token": {generateToken(t, *tc.accessToken, pk, jose.ES256)}, }, } @@ -493,7 +495,7 @@ func TestExtendedJWT_Authenticate(t *testing.T) { if tc.idToken != nil { env.s.accessTokenVerifier = &mockVerifier{Claims: *tc.accessToken} env.s.idTokenVerifier = &mockIDVerifier{Claims: *tc.idToken} - validHTTPReq.Header.Add(ExtJWTAuthorizationHeaderName, generateIDToken(*tc.idToken, pk, jose.RS256)) + validHTTPReq.Header.Add(ExtJWTAuthorizationHeaderName, generateIDToken(t, *tc.idToken, pk, jose.ES256)) } id, err := env.s.Authenticate(context.Background(), &authn.Request{ @@ -674,14 +676,14 @@ func TestVerifyRFC9068TokenFailureScenarios(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { if tc.alg == "" { - tc.alg = jose.RS256 + tc.alg = jose.ES256 } var tokenToTest string if tc.generateWrongTyp { - tokenToTest = generateIDToken(*tc.idPayload, pk, tc.alg) + tokenToTest = generateIDToken(t, *tc.idPayload, pk, tc.alg) } else { - tokenToTest = generateToken(*tc.payload, pk, tc.alg) + tokenToTest = generateToken(t, *tc.payload, pk, tc.alg) } _, err := env.s.accessTokenVerifier.Verify(context.Background(), tokenToTest) require.Error(t, err) @@ -711,24 +713,40 @@ type testEnv struct { s *ExtendedJWT } -func generateToken(payload accessTokenClaims, signingKey any, alg jose.SignatureAlgorithm) string { - signer, _ := jose.NewSigner(jose.SigningKey{Algorithm: alg, Key: signingKey}, &jose.SignerOptions{ +func generateToken(t *testing.T, payload accessTokenClaims, signingKey any, alg jose.SignatureAlgorithm) string { + signer, err := jose.NewSigner(jose.SigningKey{Algorithm: alg, Key: signingKey}, &jose.SignerOptions{ ExtraHeaders: map[jose.HeaderKey]any{ jose.HeaderType: authnlib.TokenTypeAccess, "kid": "default", }}) + if err != nil { + // For incompatible algorithm/key combinations (like RS384 with ECDSA key), + // return invalid token to test verification failure + if alg == jose.RS384 { + return "invalid.token" + } + require.NoError(t, err) + } - result, _ := jwt.Signed(signer).Claims(payload).CompactSerialize() + result, err := jwt.Signed(signer).Claims(payload).Serialize() + require.NoError(t, err) return result } -func generateIDToken(payload idTokenClaims, signingKey any, alg jose.SignatureAlgorithm) string { - signer, _ := jose.NewSigner(jose.SigningKey{Algorithm: alg, Key: signingKey}, &jose.SignerOptions{ +func generateIDToken(t *testing.T, payload idTokenClaims, signingKey any, alg jose.SignatureAlgorithm) string { + signer, err := jose.NewSigner(jose.SigningKey{Algorithm: alg, Key: signingKey}, &jose.SignerOptions{ ExtraHeaders: map[jose.HeaderKey]any{ jose.HeaderType: authnlib.TokenTypeID, "kid": "default", }}) + if err != nil { + if alg == jose.RS384 { + return "invalid.token" + } + require.NoError(t, err) + } - result, _ := jwt.Signed(signer).Claims(payload).CompactSerialize() + result, err := jwt.Signed(signer).Claims(payload).Serialize() + require.NoError(t, err) return result } diff --git a/pkg/services/authz/rbac/service_test.go b/pkg/services/authz/rbac/service_test.go index a4d732a193d..04083eb7f94 100644 --- a/pkg/services/authz/rbac/service_test.go +++ b/pkg/services/authz/rbac/service_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/go-jose/go-jose/v3/jwt" + "github.com/go-jose/go-jose/v4/jwt" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/sync/singleflight" diff --git a/pkg/services/live/live_test.go b/pkg/services/live/live_test.go index 0206107a4a3..cfead7e50ec 100644 --- a/pkg/services/live/live_test.go +++ b/pkg/services/live/live_test.go @@ -2,16 +2,21 @@ package live import ( "context" + "crypto/ecdsa" + "crypto/x509" + "encoding/pem" + "fmt" "net/http/httptest" "net/url" "testing" "time" - "github.com/go-jose/go-jose/v3" - "github.com/go-jose/go-jose/v3/jwt" + "github.com/go-jose/go-jose/v4" + "github.com/go-jose/go-jose/v4/jwt" "github.com/stretchr/testify/require" "github.com/centrifugal/centrifuge" + "github.com/grafana/grafana/pkg/api/routing" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/infra/db" @@ -379,9 +384,35 @@ func newDummyTransport(name string) *dummyTransport { return &dummyTransport{name: name} } +// There is a duplication of this function in the identity package. pkg/apimachinery/identity/requester_test.go. +// If you need to copy it, place it as a test helper function in the identity package. +var testKey = decodePrivateKey([]byte(` +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEID6lXWsmcv/UWn9SptjOThsy88cifgGIBj2Lu0M9I8tQoAoGCCqGSM49 +AwEHoUQDQgAEsf6eNnNMNhl+q7jXsbdUf3ADPh248uoFUSSV9oBzgptyokHCjJz6 +n6PKDm2W7i3S2+dAs5M5f3s7d8KiLjGZdQ== +-----END EC PRIVATE KEY----- +`)) + +func decodePrivateKey(data []byte) *ecdsa.PrivateKey { + block, _ := pem.Decode(data) + if block == nil { + panic("should include PEM block") + } + + privateKey, err := x509.ParseECPrivateKey(block.Bytes) + if err != nil { + panic(fmt.Sprintf("should be able to parse ec private key: %v", err)) + } + if privateKey.Curve.Params().Name != "P-256" { + panic("should be valid private key") + } + + return privateKey +} + func createToken(t *testing.T, exp *time.Time) string { - key := []byte("test-secret-key") - signer, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: key}, nil) + signer, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.ES256, Key: testKey}, nil) require.NoError(t, err) claims := struct { @@ -396,7 +427,7 @@ func createToken(t *testing.T, exp *time.Time) string { claims.Expiry = jwt.NewNumericDate(*exp) } - token, err := jwt.Signed(signer).Claims(claims).CompactSerialize() + token, err := jwt.Signed(signer).Claims(claims).Serialize() require.NoError(t, err) return token } diff --git a/pkg/services/oauthtoken/oauth_token.go b/pkg/services/oauthtoken/oauth_token.go index 449f319f079..0304eca46f1 100644 --- a/pkg/services/oauthtoken/oauth_token.go +++ b/pkg/services/oauthtoken/oauth_token.go @@ -7,13 +7,15 @@ import ( "strings" "time" - "github.com/go-jose/go-jose/v3/jwt" + jose "github.com/go-jose/go-jose/v4" + "github.com/go-jose/go-jose/v4/jwt" "github.com/prometheus/client_golang/prometheus" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" "golang.org/x/oauth2" claims "github.com/grafana/authlib/types" + "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/serverlock" @@ -577,7 +579,8 @@ func GetIDTokenExpiry(token *oauth2.Token) (time.Time, error) { return time.Time{}, nil } - parsedToken, err := jwt.ParseSigned(idToken) + parsedToken, err := jwt.ParseSigned(idToken, []jose.SignatureAlgorithm{jose.EdDSA, jose.HS256, jose.HS384, + jose.HS512, jose.RS512, jose.RS256, jose.ES256, jose.ES384, jose.ES512, jose.PS256, jose.PS384, jose.PS512}) if err != nil { return time.Time{}, fmt.Errorf("error parsing id token: %w", err) } diff --git a/pkg/services/signingkeys/signingkeys.go b/pkg/services/signingkeys/signingkeys.go index 1f5b370e0c7..63df929e751 100644 --- a/pkg/services/signingkeys/signingkeys.go +++ b/pkg/services/signingkeys/signingkeys.go @@ -12,7 +12,7 @@ import ( "crypto" "time" - "github.com/go-jose/go-jose/v3" + "github.com/go-jose/go-jose/v4" ) const ( diff --git a/pkg/services/signingkeys/signingkeysimpl/service.go b/pkg/services/signingkeys/signingkeysimpl/service.go index b907392cf80..a2256552604 100644 --- a/pkg/services/signingkeys/signingkeysimpl/service.go +++ b/pkg/services/signingkeys/signingkeysimpl/service.go @@ -15,7 +15,7 @@ import ( "strings" "time" - "github.com/go-jose/go-jose/v3" + "github.com/go-jose/go-jose/v4" "github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/api/routing" diff --git a/pkg/services/signingkeys/signingkeysimpl/service_test.go b/pkg/services/signingkeys/signingkeysimpl/service_test.go index 6b36ae91da7..943e903aa7c 100644 --- a/pkg/services/signingkeys/signingkeysimpl/service_test.go +++ b/pkg/services/signingkeys/signingkeysimpl/service_test.go @@ -12,7 +12,7 @@ import ( "testing" "time" - "github.com/go-jose/go-jose/v3" + "github.com/go-jose/go-jose/v4" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/pkg/services/signingkeys/signingkeystest/fake.go b/pkg/services/signingkeys/signingkeystest/fake.go index 30ca3453082..2de58c2507c 100644 --- a/pkg/services/signingkeys/signingkeystest/fake.go +++ b/pkg/services/signingkeys/signingkeystest/fake.go @@ -4,7 +4,7 @@ import ( "context" "crypto" - "github.com/go-jose/go-jose/v3" + "github.com/go-jose/go-jose/v4" ) type FakeSigningKeysService struct { diff --git a/pkg/services/signingkeys/signingkeystore/fake.go b/pkg/services/signingkeys/signingkeystore/fake.go index b78721353e7..5d5ed553698 100644 --- a/pkg/services/signingkeys/signingkeystore/fake.go +++ b/pkg/services/signingkeys/signingkeystore/fake.go @@ -4,7 +4,8 @@ import ( "context" "crypto" - "github.com/go-jose/go-jose/v3" + "github.com/go-jose/go-jose/v4" + "github.com/grafana/grafana/pkg/services/signingkeys" ) diff --git a/pkg/storage/unified/apistore/managed_test.go b/pkg/storage/unified/apistore/managed_test.go index 3af0048b14a..bbfb2448787 100644 --- a/pkg/storage/unified/apistore/managed_test.go +++ b/pkg/storage/unified/apistore/managed_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/go-jose/go-jose/v3/jwt" + "github.com/go-jose/go-jose/v4/jwt" "github.com/stretchr/testify/require" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -12,6 +12,7 @@ import ( authnlib "github.com/grafana/authlib/authn" authtypes "github.com/grafana/authlib/types" + dashboard "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1" provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/apimachinery/identity" diff --git a/pkg/storage/unified/resource/client.go b/pkg/storage/unified/resource/client.go index 9688a59d77d..c9e9ffbe07a 100644 --- a/pkg/storage/unified/resource/client.go +++ b/pkg/storage/unified/resource/client.go @@ -2,16 +2,18 @@ package resource import ( "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" "crypto/tls" - "encoding/base64" - "encoding/json" "fmt" "log/slog" "net/http" "github.com/fullstorydev/grpchan" "github.com/fullstorydev/grpchan/inprocgrpc" - "github.com/go-jose/go-jose/v3/jwt" + "github.com/go-jose/go-jose/v4" + "github.com/go-jose/go-jose/v4/jwt" grpcAuth "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/trace" @@ -192,6 +194,23 @@ func ProvideInProcExchanger() authnlib.StaticTokenExchanger { } func createInProcToken() (string, error) { + // Generate ES256 private key + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return "", fmt.Errorf("failed to generate ES256 private key: %w", err) + } + + // Create signer with ES256 algorithm + signer, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.ES256, Key: privateKey}, &jose.SignerOptions{ + ExtraHeaders: map[jose.HeaderKey]interface{}{ + jose.HeaderKey("typ"): authnlib.TokenTypeAccess, + }, + }) + if err != nil { + return "", fmt.Errorf("failed to create signer: %w", err) + } + + // Create claims claims := authnlib.Claims[authnlib.AccessTokenClaims]{ Claims: jwt.Claims{ Issuer: "grafana", @@ -205,18 +224,11 @@ func createInProcToken() (string, error) { }, } - header, err := json.Marshal(map[string]string{ - "alg": "none", - "typ": authnlib.TokenTypeAccess, - }) + // Sign and create the JWT + token, err := jwt.Signed(signer).Claims(claims).Serialize() if err != nil { - return "", err + return "", fmt.Errorf("failed to sign JWT: %w", err) } - payload, err := json.Marshal(claims) - if err != nil { - return "", err - } - - return base64.RawURLEncoding.EncodeToString(header) + "." + base64.RawURLEncoding.EncodeToString(payload) + ".", nil + return token, nil } diff --git a/pkg/storage/unified/sql/test/integration_test.go b/pkg/storage/unified/sql/test/integration_test.go index 90a3a54d635..b8ccd7cef1b 100644 --- a/pkg/storage/unified/sql/test/integration_test.go +++ b/pkg/storage/unified/sql/test/integration_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/go-jose/go-jose/v3/jwt" + "github.com/go-jose/go-jose/v4/jwt" "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/require" diff --git a/pkg/storage/unified/testing/storage_backend.go b/pkg/storage/unified/testing/storage_backend.go index d77276f88c8..1a13c310edd 100644 --- a/pkg/storage/unified/testing/storage_backend.go +++ b/pkg/storage/unified/testing/storage_backend.go @@ -10,7 +10,7 @@ import ( "testing" "time" - "github.com/go-jose/go-jose/v3/jwt" + "github.com/go-jose/go-jose/v4/jwt" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" From 268888da311e5b011704637102722faa3914e681 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 15 Sep 2025 11:13:00 +0000 Subject: [PATCH 27/33] Update dependency @leeoniya/ufuzzy to v1.0.19 (#111080) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- packages/grafana-data/package.json | 2 +- packages/grafana-flamegraph/package.json | 2 +- packages/grafana-prometheus/package.json | 2 +- yarn.lock | 16 ++++++++-------- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index b269774ebd8..f042fc68c71 100644 --- a/package.json +++ b/package.json @@ -293,7 +293,7 @@ "@grafana/ui": "workspace:*", "@hello-pangea/dnd": "18.0.1", "@kusto/monaco-kusto": "^10.0.0", - "@leeoniya/ufuzzy": "1.0.18", + "@leeoniya/ufuzzy": "1.0.19", "@lezer/common": "1.2.3", "@lezer/highlight": "1.2.1", "@lezer/lr": "1.4.2", diff --git a/packages/grafana-data/package.json b/packages/grafana-data/package.json index bb6a57974c8..e83364f687e 100644 --- a/packages/grafana-data/package.json +++ b/packages/grafana-data/package.json @@ -58,7 +58,7 @@ "@braintree/sanitize-url": "7.0.1", "@grafana/i18n": "12.3.0-pre", "@grafana/schema": "12.3.0-pre", - "@leeoniya/ufuzzy": "1.0.18", + "@leeoniya/ufuzzy": "1.0.19", "@types/d3-interpolate": "^3.0.0", "@types/string-hash": "1.1.3", "@types/systemjs": "6.15.3", diff --git a/packages/grafana-flamegraph/package.json b/packages/grafana-flamegraph/package.json index 5d9087e4516..bc3a226afdd 100644 --- a/packages/grafana-flamegraph/package.json +++ b/packages/grafana-flamegraph/package.json @@ -46,7 +46,7 @@ "@emotion/css": "11.13.5", "@grafana/data": "12.3.0-pre", "@grafana/ui": "12.3.0-pre", - "@leeoniya/ufuzzy": "1.0.18", + "@leeoniya/ufuzzy": "1.0.19", "d3": "^7.8.5", "lodash": "4.17.21", "react": "18.3.1", diff --git a/packages/grafana-prometheus/package.json b/packages/grafana-prometheus/package.json index be492d4c455..ca1c58148c3 100644 --- a/packages/grafana-prometheus/package.json +++ b/packages/grafana-prometheus/package.json @@ -49,7 +49,7 @@ "@grafana/schema": "12.3.0-pre", "@grafana/ui": "12.3.0-pre", "@hello-pangea/dnd": "18.0.1", - "@leeoniya/ufuzzy": "1.0.18", + "@leeoniya/ufuzzy": "1.0.19", "@lezer/common": "1.2.3", "@lezer/highlight": "1.2.1", "@lezer/lr": "1.4.2", diff --git a/yarn.lock b/yarn.lock index 4a0e5b58b1e..af82d792b0b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3042,7 +3042,7 @@ __metadata: "@braintree/sanitize-url": "npm:7.0.1" "@grafana/i18n": "npm:12.3.0-pre" "@grafana/schema": "npm:12.3.0-pre" - "@leeoniya/ufuzzy": "npm:1.0.18" + "@leeoniya/ufuzzy": "npm:1.0.19" "@rollup/plugin-node-resolve": "npm:16.0.1" "@types/d3-interpolate": "npm:^3.0.0" "@types/history": "npm:4.7.11" @@ -3190,7 +3190,7 @@ __metadata: "@emotion/css": "npm:11.13.5" "@grafana/data": "npm:12.3.0-pre" "@grafana/ui": "npm:12.3.0-pre" - "@leeoniya/ufuzzy": "npm:1.0.18" + "@leeoniya/ufuzzy": "npm:1.0.19" "@rollup/plugin-node-resolve": "npm:16.0.1" "@testing-library/dom": "npm:10.4.1" "@testing-library/jest-dom": "npm:^6.1.2" @@ -3423,7 +3423,7 @@ __metadata: "@grafana/schema": "npm:12.3.0-pre" "@grafana/ui": "npm:12.3.0-pre" "@hello-pangea/dnd": "npm:18.0.1" - "@leeoniya/ufuzzy": "npm:1.0.18" + "@leeoniya/ufuzzy": "npm:1.0.19" "@lezer/common": "npm:1.2.3" "@lezer/highlight": "npm:1.2.1" "@lezer/lr": "npm:1.4.2" @@ -4782,10 +4782,10 @@ __metadata: 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" - checksum: 10/3d7e160a3a21cdcdf0c0b78893340b3231b905ab4787e63d39068ee0ad75cc71ae99572c2117cd3466e3d32bfe0290b97d4ccda9e522f7998b24a04c2c466a31 +"@leeoniya/ufuzzy@npm:1.0.19, @leeoniya/ufuzzy@npm:^1.0.16": + version: 1.0.19 + resolution: "@leeoniya/ufuzzy@npm:1.0.19" + checksum: 10/097153dbdc1eb36939513e275e63f573d11f06adfbc885d80b90090996ecdb77fc7c643d53a22f4cee968f4d435e62cc2284d020161193fb6875b3bd73566ffc languageName: node linkType: hard @@ -18176,7 +18176,7 @@ __metadata: "@grafana/ui": "workspace:*" "@hello-pangea/dnd": "npm:18.0.1" "@kusto/monaco-kusto": "npm:^10.0.0" - "@leeoniya/ufuzzy": "npm:1.0.18" + "@leeoniya/ufuzzy": "npm:1.0.19" "@lezer/common": "npm:1.2.3" "@lezer/highlight": "npm:1.2.1" "@lezer/lr": "npm:1.4.2" From 898b0fc1bbaff647563fb934ecb331598f7af01e Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Mon, 15 Sep 2025 13:40:45 +0200 Subject: [PATCH 28/33] DashboardProfiler: Add long frame detection with LoAF API integration (#110443) * feat: Add Long Animation Frame API support to dashboard performance monitoring * Update dashboard profiler integration for long frame detection - Remove LongFrameConfig parameter from SceneRenderProfiler constructor - Update documentation to reflect LoAF-first detection strategy with 50ms threshold - Remove references to configurable thresholds and script attribution - Update console output examples to match new structured logging format - Add related documentation reference to scenes PR #1235 * Update to scenes canary version with long frame detection - Upgrade @grafana/scenes to 6.33.1--canary.1235.17401388269.0 - Upgrade @grafana/scenes-react to 6.33.1--canary.1235.17401388269.0 - Includes long frame detection implementation from PR #1235 - Update yarn.lock with new dependencies * feat(performance): add PanelPerformanceData interface for panel-level metrics - Create comprehensive panel performance data structure - Include timing metrics, performance counters, and context data - Add pluginLoadedFromCache flag to track cache usage - Part of panel-level performance attribution implementation * scenes bump * Revert "feat(performance): add PanelPerformanceData interface for panel-level metrics" This reverts commit 854770167282ab71b38f00a837dc77217bc27508. * fix lock * Fix lock --- .../dashboard/services/DashboardProfiler.ts | 2 + .../dashboard-render-performance-profiling.md | 98 ++++++++++++++++++- 2 files changed, 98 insertions(+), 2 deletions(-) diff --git a/public/app/features/dashboard/services/DashboardProfiler.ts b/public/app/features/dashboard/services/DashboardProfiler.ts index e62a8382d24..af198ab2750 100644 --- a/public/app/features/dashboard/services/DashboardProfiler.ts +++ b/public/app/features/dashboard/services/DashboardProfiler.ts @@ -21,6 +21,8 @@ export function getDashboardInteractionCallback(uid: string, title: string) { totalJSHeapSize: e.totalJSHeapSize, usedJSHeapSize: e.usedJSHeapSize, jsHeapSizeLimit: e.jsHeapSizeLimit, + longFramesCount: e.longFramesCount, + longFramesTotalTime: e.longFramesTotalTime, timeSinceBoot: performance.measure('time_since_boot', 'frontend_boot_js_done_time_seconds').duration, }; diff --git a/public/app/features/dashboard/services/dashboard-render-performance-profiling.md b/public/app/features/dashboard/services/dashboard-render-performance-profiling.md index 2320e28be16..80f6fc522f6 100644 --- a/public/app/features/dashboard/services/dashboard-render-performance-profiling.md +++ b/public/app/features/dashboard/services/dashboard-render-performance-profiling.md @@ -2,6 +2,29 @@ This documentation describes the dashboard render performance metrics exposed from Grafana's frontend. +## Table of Contents + +- [Overview](#overview) +- [Configuration](#configuration) + - [Enabling Performance Metrics](#enabling-performance-metrics) +- [Tracked Interactions](#tracked-interactions) + - [Core Performance-Tracked Interactions](#core-performance-tracked-interactions) + - [Interaction Origin Mapping](#interaction-origin-mapping) +- [Profiling Implementation](#profiling-implementation) + - [Profile Data Structure](#profile-data-structure) + - [Collected Metrics](#collected-metrics) +- [Debugging and Development](#debugging-and-development) + - [Enable Profiler Debug Logging](#enable-profiler-debug-logging) + - [Enable Echo Service Debug Logging](#enable-echo-service-debug-logging) + - [Browser Performance Profiler](#browser-performance-profiler) +- [Analytics Integration](#analytics-integration) + - [Interaction Reporting](#interaction-reporting) + - [Data Collection](#data-collection) +- [Implementation Details](#implementation-details) + - [Long Frame Detection](#long-frame-detection) + - [Tab Inactivity Handling](#tab-inactivity-handling) +- [Related Documentation](#related-documentation) + ## Overview The exposed dashboard performance metrics feature provides comprehensive tracking and profiling of dashboard interactions, allowing administrators and developers to analyze dashboard render performance, user interactions, and identify performance bottlenecks. @@ -69,6 +92,8 @@ const payload = { totalJSHeapSize: e.totalJSHeapSize, usedJSHeapSize: e.usedJSHeapSize, jsHeapSizeLimit: e.jsHeapSizeLimit, + longFramesCount: e.longFramesCount, + longFramesTotalTime: e.longFramesTotalTime, timeSinceBoot: performance.measure('time_since_boot', 'frontend_boot_js_done_time_seconds').duration, }; @@ -101,6 +126,8 @@ interface SceneInteractionProfileEvent { jsHeapSizeLimit: number; // JavaScript heap size limit startTs: number; // Profile start timestamp endTs: number; // Profile end timestamp + longFramesCount: number; // Number of long frames (>50ms threshold) + longFramesTotalTime: number; // Total time of long frames during interaction } ``` @@ -113,6 +140,8 @@ For each tracked interaction, the system collects: - `duration`: Total interaction time from start to finish - `networkDuration`: Time spent on network requests (API calls, data fetching) - `processingTime`: Client-side processing time calculated as `duration - networkDuration` + - `longFramesCount`: Number of frames that exceeded the 50ms threshold + - `longFramesTotalTime`: Cumulative time of all long frames during the interaction - **Memory Metrics**: JavaScript heap usage statistics - **Timing Information**: Time since boot, profile start and end timestamps - **Interaction Context**: Type of user interaction @@ -124,6 +153,10 @@ The performance metrics provide detailed insights into where time is spent durin - **Total Duration (`duration`)**: Complete time from interaction start to completion - **Network Time (`networkDuration`)**: Time spent waiting for server responses (data source queries, API calls) - **Processing Time (`processingTime`)**: Time spent on client-side operations (rendering, computations, DOM updates) +- **Long Frames (`longFramesCount` & `longFramesTotalTime`)**: Frames exceeding 50ms threshold indicate potential UI jank or performance issues. These metrics help identify interactions causing poor user experience: + - `longFramesCount`: The number of frames that exceeded the 50ms threshold + - `longFramesTotalTime`: The total accumulated time of all long frames, indicating the severity of performance issues + - **Detection Method**: Automatically uses Long Animation Frame API when available (Chrome 123+), falls back to manual tracking for broader browser support ## Debugging and Development @@ -140,9 +173,20 @@ localStorage.setItem('grafana.debug.scenes', 'true'); When debug logging is enabled, you'll see console logs for each profiling event: ``` -SceneRenderProfiler: Profile started: {origin: , crumbs: Array(0)} +SceneRenderProfiler: Profile started[clean] + ├─ Origin: dashboard_view + └─ Timestamp: 1072.5ms +LongFrameDetector: Started tracking with LoAF API method, threshold: 50ms ... // intermediate steps adding profile crumbs -SceneRenderProfiler: Stopped recording, total measured time (network included): 2123 +LongFrameDetector: Long frame detected (LoAF): 67.4ms at 1071.5ms +LongFrameDetector: Long frame detected (LoAF): 76.3ms at 1139.8ms +... // more long frame detections +SceneRenderProfiler: Profile completed + ├─ Timestamp: 3530.6ms + ├─ Total time: 156.8ms + ├─ Slow frames: 16.3ms (1 frames) + └─ Long frames: 143.7ms (2 frames) +SceneRenderProfiler: Stopped long frame detection - profile complete at 3530.6ms ``` ### Enable Echo Service Debug Logging @@ -194,6 +238,8 @@ The system reports the following data for each interaction: totalJSHeapSize: number, // Memory metrics usedJSHeapSize: number, jsHeapSizeLimit: number, + longFramesCount: number, // Number of long frames (>50ms threshold) + longFramesTotalTime: number, // Total time of all long frames timeSinceBoot: number // Time since frontend boot } ``` @@ -202,6 +248,53 @@ The system reports the following data for each interaction: The profiler is integrated into dashboard creation paths and uses a singleton pattern to share profiler instances across dashboard reloads. The performance tracking is implemented using the `SceneRenderProfiler` from the `@grafana/scenes` library. +### Long Frame Detection + +The profiler uses the Long Animation Frame (LoAF) API when available to monitor frame rendering performance during dashboard interactions: + +#### Primary Method: Long Animation Frame API + +- **Browser Support**: Chrome 123+ (automatically detected) +- **Threshold**: 50ms (standard LoAF threshold) +- **Benefits**: + - Browser-level accuracy and performance + - Standards-based implementation + - More efficient than manual tracking + - Automatic buffering control for real-time detection + +#### Fallback Method: Manual Frame Tracking + +- **Browser Support**: All browsers +- **Threshold**: 50ms (same as LoAF threshold) +- **Used when**: LoAF API is not available +- **Implementation**: Uses requestAnimationFrame for frame monitoring + +Both methods track: + +- **Count**: Number of frames exceeding the threshold +- **Total Time**: Cumulative duration of all long frames + +#### Debug Output + +With LoAF API: + +``` +LongFrameDetector: Long frame detected (LoAF): 67.4ms at 1234.5ms +``` + +With manual fallback: + +``` +LongFrameDetector: Long frame detected (manual): 38.2ms (threshold: 50ms) +``` + +This metric is particularly valuable for: + +- Detecting rendering performance issues that impact user experience +- Identifying when interactions cause UI jank or frame drops +- Measuring the impact of performance optimizations on frame rendering +- Comparing performance across different browsers and environments + ### Tab Inactivity Handling To prevent meaningless profiling data when users switch browser tabs, the `SceneRenderProfiler` implements dual protection mechanisms: @@ -325,3 +418,4 @@ Without profile isolation, these scenarios could result in profiles that never c - [PR #1211 - SceneRenderProfiler: Improve profiler accuracy by adding cancellation and skipping inactive tabs](https://github.com/grafana/scenes/pull/1211) - [PR #1212 - SceneQueryController: Fix profiler query controller registration on scene re-activation](https://github.com/grafana/scenes/pull/1212) - [PR #1225 - SceneRenderProfiler: Handle overlapping profiles by cancelling previous profile](https://github.com/grafana/scenes/pull/1225) +- [PR #1235 - Implement long frame detection with LoAF API and manual fallback](https://github.com/grafana/scenes/pull/1235) From 90f682151a2d7e65b8d309b7b67e804fd742795a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 15 Sep 2025 11:46:50 +0000 Subject: [PATCH 29/33] Update dependency @types/jquery to v3.5.33 (#111085) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- packages/grafana-ui/package.json | 2 +- yarn.lock | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index f042fc68c71..111357398bc 100644 --- a/package.json +++ b/package.json @@ -124,7 +124,7 @@ "@types/history": "4.7.11", "@types/ini": "^4", "@types/jest": "29.5.14", - "@types/jquery": "3.5.32", + "@types/jquery": "3.5.33", "@types/js-yaml": "^4.0.5", "@types/jsurl": "^1.2.28", "@types/lodash": "4.17.20", diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index 25f637bbff7..17d0fce689f 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -80,7 +80,7 @@ "@react-aria/overlays": "3.28.0", "@react-aria/utils": "3.30.0", "@tanstack/react-virtual": "^3.5.1", - "@types/jquery": "3.5.32", + "@types/jquery": "3.5.33", "@types/lodash": "4.17.20", "@types/react-table": "7.7.20", "calculate-size": "1.1.1", diff --git a/yarn.lock b/yarn.lock index af82d792b0b..8b63df1f3ac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3700,7 +3700,7 @@ __metadata: "@types/hoist-non-react-statics": "npm:3.3.7" "@types/is-hotkey": "npm:0.1.10" "@types/jest": "npm:29.5.14" - "@types/jquery": "npm:3.5.32" + "@types/jquery": "npm:3.5.33" "@types/lodash": "npm:4.17.20" "@types/mock-raf": "npm:1.0.6" "@types/node": "npm:22.17.0" @@ -9538,12 +9538,12 @@ __metadata: languageName: node linkType: hard -"@types/jquery@npm:3.5.32": - version: 3.5.32 - resolution: "@types/jquery@npm:3.5.32" +"@types/jquery@npm:3.5.33": + version: 3.5.33 + resolution: "@types/jquery@npm:3.5.33" dependencies: "@types/sizzle": "npm:*" - checksum: 10/2c67cac338828870ead5c5e608f5fa5ab8101598ed4572cf49b58c342adffe8918d2e2fc94d7954e6b98a889cef8c3f4e6f44b8fecb75e80854b0f9cf9dd18a1 + checksum: 10/9a9e2cddc584f9afa1970b0febac0b65bed6d8084baf9655346f5787ee1b25975a0f259b0c81edc7757fbb92d584ed2d1b39804ab78ab0238407c7e4b0376011 languageName: node linkType: hard @@ -18231,7 +18231,7 @@ __metadata: "@types/history": "npm:4.7.11" "@types/ini": "npm:^4" "@types/jest": "npm:29.5.14" - "@types/jquery": "npm:3.5.32" + "@types/jquery": "npm:3.5.33" "@types/js-yaml": "npm:^4.0.5" "@types/jsurl": "npm:^1.2.28" "@types/lodash": "npm:4.17.20" From 5d48747fea5d02a7d23b4d7e8135a3d928906979 Mon Sep 17 00:00:00 2001 From: Andreas Christou Date: Mon, 15 Sep 2025 13:56:08 +0200 Subject: [PATCH 30/33] Graphite: Backend version endpoint (#110774) * Add lint rules * Backend decoupling - Add standalone files - Add graphite query type - Add logger to Service - Create logger in the ProvideService method - Use a pointer for the HTTP client provider - Update logger usage everywhere - Update tracer type - Replace simplejson with json - Add dummy CallResource and CheckHealth methods - Update tests * Update ConfigEditor imports * Update types imports * Update datasource - Switch to using semver package - Update imports * Update store imports * Update helper imports and notification creation * Update context import * Update version numbers and logic * Copy array_move from core * Test updates * Add required files and update plugin.json * Update core references and packages * Remove commented code * Update wire * Lint * Fix import * Copy null type * More lint * Update snapshot * Refactor backend - Split query logic into separate file - Move utils to separate file * Add health-check logic - Support backend healthcheck if the FF is enabled * Remove query import support as unneeded * Add test * Add util function for decoding responses * Add events types * Add resource handler * Add events handler and generic resource req handler * Tests * Update frontend - Add types - Update events function to support backend requests * Lint and typing * Lint * Add metrics find endpoint - Add types - Add generic response parser - Add endpoint - Tests * Update FE functoin to use backend endpoint * Lint * Simplify request * Update test * Metrics expand type * Extract shared logic and add metric expand endpoint * Update tests * Call metric expand from backend * Rename type for clarity * Add get resource req handler * Refactor doGraphiteRequest, parseResponse Update tests * Migrate functions endpoint to backend * Support tags autocomplete in backend - Add tests - Add types - Remove unneeded comments * Support tag values autocomplete - Remove unused frontend endpoints - Add types - Update tests * Support the version endpoint * Add tests * Review * Review * Fix packages * Format * Fix merge issues * Review * Fix undefined values * Extract request creation - Add method for create requests generically with tests - Replace usage in query method - Update usages in resource handlers - Update tests - Update types --- pkg/tsdb/graphite/resource_handler.go | 28 +++++-- pkg/tsdb/graphite/resource_handler_test.go | 80 +++++++++++++++++++ .../plugins/datasource/graphite/datasource.ts | 8 +- 3 files changed, 109 insertions(+), 7 deletions(-) diff --git a/pkg/tsdb/graphite/resource_handler.go b/pkg/tsdb/graphite/resource_handler.go index dfd1a3c6568..2651c320d65 100644 --- a/pkg/tsdb/graphite/resource_handler.go +++ b/pkg/tsdb/graphite/resource_handler.go @@ -27,7 +27,7 @@ func (s *Service) newResourceMux() *http.ServeMux { mux.HandleFunc("/functions", handleResourceReq(s.handleFunctions, s)) mux.HandleFunc("/tags/autoComplete/tags", handleResourceReq(s.handleTagsAutocomplete, s)) mux.HandleFunc("/tags/autoComplete/values", handleResourceReq(s.handleTagValuesAutocomplete, s)) - + mux.HandleFunc("/version", handleResourceReq(s.handleVersion, s)) return mux } @@ -99,7 +99,6 @@ func (s *Service) handleEvents(ctx context.Context, dsInfo *datasourceInfo, even req, err := s.createRequest(ctx, dsInfo, URLParams{ SubPath: "events/get_data", - Method: http.MethodGet, QueryParams: queryParams, }) if err != nil { @@ -179,7 +178,6 @@ func (s *Service) handleMetricsExpand(ctx context.Context, dsInfo *datasourceInf req, err := s.createRequest(ctx, dsInfo, URLParams{ SubPath: "metrics/expand", - Method: http.MethodGet, QueryParams: queryParams, }) if err != nil { @@ -215,7 +213,6 @@ func (s *Service) handleTagsAutocomplete(ctx context.Context, dsInfo *datasource } req, err := s.createRequest(ctx, dsInfo, URLParams{ SubPath: "tags/autoComplete/tags", - Method: http.MethodGet, QueryParams: queryParams, }) if err != nil { @@ -247,7 +244,6 @@ func (s *Service) handleTagValuesAutocomplete(ctx context.Context, dsInfo *datas req, err := s.createRequest(ctx, dsInfo, URLParams{ SubPath: "tags/autoComplete/values", - Method: http.MethodGet, QueryParams: queryParams, }) if err != nil { @@ -267,10 +263,30 @@ func (s *Service) handleTagValuesAutocomplete(ctx context.Context, dsInfo *datas return tagValuesResponse, statusCode, nil } +func (s *Service) handleVersion(ctx context.Context, dsInfo *datasourceInfo, _ *any) ([]byte, int, error) { + req, err := s.createRequest(ctx, dsInfo, URLParams{ + SubPath: "version", + }) + if err != nil { + return nil, http.StatusInternalServerError, fmt.Errorf("failed to create version request %v", err) + } + + version, _, statusCode, err := doGraphiteRequest[string](ctx, dsInfo, s.logger, req, false) + if err != nil { + return nil, statusCode, fmt.Errorf("version request failed: %v", err) + } + + versionResponse, err := json.Marshal(version) + if err != nil { + return nil, http.StatusInternalServerError, fmt.Errorf("failed to marshal version response: %s", err) + } + + return versionResponse, statusCode, nil +} + func (s *Service) handleFunctions(ctx context.Context, dsInfo *datasourceInfo, _ *any) ([]byte, int, error) { req, err := s.createRequest(ctx, dsInfo, URLParams{ SubPath: "functions", - Method: http.MethodGet, }) if err != nil { return nil, http.StatusInternalServerError, fmt.Errorf("failed to create functions request %v", err) diff --git a/pkg/tsdb/graphite/resource_handler_test.go b/pkg/tsdb/graphite/resource_handler_test.go index c4ba2e201d2..66777f95072 100644 --- a/pkg/tsdb/graphite/resource_handler_test.go +++ b/pkg/tsdb/graphite/resource_handler_test.go @@ -620,6 +620,86 @@ func TestHandleTagValuesAutocomplete(t *testing.T) { }) } } + +func TestHandleVersion(t *testing.T) { + tests := []struct { + name string + responseBody string + statusCode int + expectError bool + errorContains string + expectedData string + }{ + { + name: "successful version request", + responseBody: `"1.1.10"`, + statusCode: 200, + expectedData: "1.1.10", + }, + { + name: "version with build info", + responseBody: `"1.1.10-pre1"`, + statusCode: 200, + expectedData: "1.1.10-pre1", + }, + { + name: "version request server error - invalid JSON causes parse error", + responseBody: `{"error": "internal error"}`, + statusCode: 500, + expectError: true, + errorContains: "version request failed", + }, + { + name: "version request not found - invalid JSON causes parse error", + responseBody: `{"error": "not found"}`, + statusCode: 404, + expectError: true, + errorContains: "version request failed", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockTransport := &mockRoundTripper{ + respBody: []byte(tt.responseBody), + status: tt.statusCode, + } + + dsInfo := &datasourceInfo{ + HTTPClient: &http.Client{Transport: mockTransport}, + URL: "http://graphite.example.com", + } + + service := &Service{ + logger: log.NewNullLogger(), + } + + result, statusCode, err := service.handleVersion(context.Background(), dsInfo, nil) + + if tt.expectError { + assert.Error(t, err) + if tt.errorContains != "" { + assert.Contains(t, err.Error(), tt.errorContains) + } + } else { + assert.NoError(t, err) + assert.Equal(t, tt.statusCode, statusCode) + + var version string + err = json.Unmarshal(result, &version) + assert.NoError(t, err) + assert.Equal(t, tt.expectedData, version) + } + + if !tt.expectError { + expectedURL := "http://graphite.example.com/version" + assert.Equal(t, expectedURL, mockTransport.lastRequest.URL.String()) + assert.Equal(t, http.MethodGet, mockTransport.lastRequest.Method) + } + }) + } +} + func TestHandleFunctions(t *testing.T) { tests := []struct { name string diff --git a/public/app/plugins/datasource/graphite/datasource.ts b/public/app/plugins/datasource/graphite/datasource.ts index 2c09afd4b83..93fa556c428 100644 --- a/public/app/plugins/datasource/graphite/datasource.ts +++ b/public/app/plugins/datasource/graphite/datasource.ts @@ -871,7 +871,7 @@ export class GraphiteDatasource return lastValueFrom(this.doGraphiteRequest(httpOptions).pipe(mapToTags())); } - getVersion(optionalOptions: any) { + async getVersion(optionalOptions: any) { const options = optionalOptions || {}; const httpOptions = { @@ -880,6 +880,12 @@ export class GraphiteDatasource requestId: options.requestId, }; + if (config.featureToggles.graphiteBackendMode) { + const version = await this.getResource('version'); + const semver = new SemVer(version); + return valid(semver) ? version : ''; + } + return lastValueFrom( this.doGraphiteRequest(httpOptions).pipe( map((results: FetchResponse) => { From fd8c7fbc2268c2030aa9ef18bac9d367d1062d62 Mon Sep 17 00:00:00 2001 From: Bogdan Matei Date: Mon, 15 Sep 2025 15:02:48 +0300 Subject: [PATCH 31/33] Dashboard Datasource: Fix type assertion (#111082) --- eslint-suppressions.json | 5 ----- public/app/plugins/datasource/dashboard/datasource.ts | 6 ++++-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/eslint-suppressions.json b/eslint-suppressions.json index 91901fdad41..00a787b4cb6 100644 --- a/eslint-suppressions.json +++ b/eslint-suppressions.json @@ -3935,11 +3935,6 @@ "count": 4 } }, - "public/app/plugins/datasource/dashboard/datasource.ts": { - "@typescript-eslint/consistent-type-assertions": { - "count": 1 - } - }, "public/app/plugins/datasource/dashboard/runSharedRequest.ts": { "@typescript-eslint/consistent-type-assertions": { "count": 2 diff --git a/public/app/plugins/datasource/dashboard/datasource.ts b/public/app/plugins/datasource/dashboard/datasource.ts index 8f945bad984..9969c9afe7f 100644 --- a/public/app/plugins/datasource/dashboard/datasource.ts +++ b/public/app/plugins/datasource/dashboard/datasource.ts @@ -21,7 +21,7 @@ import { DrilldownsApplicability, } from '@grafana/data'; import { config } from '@grafana/runtime'; -import { SceneDataProvider, SceneDataTransformer, SceneObject } from '@grafana/scenes'; +import { isSceneObject, SceneDataProvider, SceneDataTransformer, SceneObject } from '@grafana/scenes'; import { activateSceneObjectAndParentTree, findVizPanelByKey, @@ -46,7 +46,9 @@ export class DashboardDatasource extends DataSourceApi { query(options: DataQueryRequest): Observable { const sceneScopedVar: ScopedVar | undefined = options.scopedVars?.__sceneObject; - let scene: SceneObject | undefined = sceneScopedVar ? (sceneScopedVar.value.valueOf() as SceneObject) : undefined; + const sceneScopedVarValue: unknown | undefined = sceneScopedVar?.value.valueOf(); + const scene: SceneObject | undefined = + sceneScopedVarValue && isSceneObject(sceneScopedVarValue) ? sceneScopedVarValue : undefined; if (!scene) { throw new Error('Can only be called from a scene'); From 4989c126958a690024389410bb3cf9fb03fdd850 Mon Sep 17 00:00:00 2001 From: Levente Balogh Date: Mon, 15 Sep 2025 14:17:35 +0200 Subject: [PATCH 32/33] Dashboard Controls: Make it possible to hide dashboard controls from the URL (#111001) * feat: make it possible to hide dashboard-controls from the URL * tests: update tests with the new url query param --- .../scene/DashboardControls.test.tsx | 91 ++++++++++++++++--- .../scene/DashboardControls.tsx | 41 +++++++-- 2 files changed, 110 insertions(+), 22 deletions(-) diff --git a/public/app/features/dashboard-scene/scene/DashboardControls.test.tsx b/public/app/features/dashboard-scene/scene/DashboardControls.test.tsx index dc4c492bf92..c01808f411e 100644 --- a/public/app/features/dashboard-scene/scene/DashboardControls.test.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardControls.test.tsx @@ -14,17 +14,56 @@ describe('DashboardControls', () => { expect(scene.state.refreshPicker).toBeDefined(); }); - it('should return if time controls are hidden', () => { - const scene = buildTestScene({ - hideTimeControls: false, - hideVariableControls: false, - hideLinksControls: false, + describe('.hasControls()', () => { + it('should return TRUE if any of the controls are available', () => { + const scene = buildTestScene({ + hideTimeControls: false, + hideVariableControls: false, + hideLinksControls: false, + hideDashboardControls: false, + }); + + // All controls visible + expect(scene.hasControls()).toBeTruthy(); + + // Hiding time controls + scene.setState({ + hideTimeControls: true, + hideVariableControls: false, + hideLinksControls: false, + hideDashboardControls: false, + }); + expect(scene.hasControls()).toBeTruthy(); + + // Hide variable controls as well + scene.setState({ + hideTimeControls: true, + hideVariableControls: true, + hideLinksControls: false, + hideDashboardControls: false, + }); + expect(scene.hasControls()).toBeTruthy(); + + // Hide link controls as well + scene.setState({ + hideTimeControls: true, + hideVariableControls: true, + hideLinksControls: true, + hideDashboardControls: false, + }); + expect(scene.hasControls()).toBeTruthy(); + }); + + it('should return FALSE if no controls are available', () => { + const scene = buildTestScene({ + hideTimeControls: true, + hideVariableControls: true, + hideLinksControls: true, + hideDashboardControls: true, + }); + + expect(scene.hasControls()).toBeFalsy(); }); - expect(scene.hasControls()).toBeTruthy(); - scene.setState({ hideTimeControls: true }); - expect(scene.hasControls()).toBeTruthy(); - scene.setState({ hideVariableControls: true, hideLinksControls: true }); - expect(scene.hasControls()).toBeFalsy(); }); }); @@ -52,10 +91,11 @@ describe('DashboardControls', () => { hideTimeControls: true, hideVariableControls: true, hideLinksControls: true, + hideDashboardControls: true, }); const renderer = render(); - expect(await renderer.queryByTestId(selectors.pages.Dashboard.Controls)).not.toBeInTheDocument(); + expect(renderer.queryByTestId(selectors.pages.Dashboard.Controls)).not.toBeInTheDocument(); }); }); @@ -63,7 +103,12 @@ describe('DashboardControls', () => { it('should return keys', () => { const scene = buildTestScene(); // @ts-expect-error - expect(scene._urlSync.getKeys()).toEqual(['_dash.hideTimePicker', '_dash.hideVariables', '_dash.hideLinks']); + expect(scene._urlSync.getKeys()).toEqual([ + '_dash.hideTimePicker', + '_dash.hideVariables', + '_dash.hideLinks', + '_dash.hideDashboardControls', + ]); }); it('should not return url state for hide flags', () => { @@ -73,6 +118,7 @@ describe('DashboardControls', () => { hideTimeControls: true, hideVariableControls: true, hideLinksControls: true, + hideDashboardControls: true, }); expect(scene.getUrlState()).toEqual({}); }); @@ -83,18 +129,22 @@ describe('DashboardControls', () => { '_dash.hideTimePicker': 'true', '_dash.hideVariables': 'true', '_dash.hideLinks': 'true', + '_dash.hideDashboardControls': 'true', }); expect(scene.state.hideTimeControls).toBeTruthy(); expect(scene.state.hideVariableControls).toBeTruthy(); expect(scene.state.hideLinksControls).toBeTruthy(); + expect(scene.state.hideDashboardControls).toBeTruthy(); scene.updateFromUrl({ '_dash.hideTimePicker': '', '_dash.hideVariables': '', '_dash.hideLinks': '', + '_dash.hideDashboardControls': '', }); expect(scene.state.hideTimeControls).toBeTruthy(); expect(scene.state.hideVariableControls).toBeTruthy(); expect(scene.state.hideLinksControls).toBeTruthy(); + expect(scene.state.hideDashboardControls).toBeTruthy(); }); it('should not override state if no new state comes from url', () => { @@ -102,11 +152,13 @@ describe('DashboardControls', () => { hideTimeControls: true, hideVariableControls: true, hideLinksControls: true, + hideDashboardControls: true, }); scene.updateFromUrl({}); expect(scene.state.hideTimeControls).toBeTruthy(); expect(scene.state.hideVariableControls).toBeTruthy(); expect(scene.state.hideLinksControls).toBeTruthy(); + expect(scene.state.hideDashboardControls).toBeTruthy(); }); it('should not call setState if no changes', () => { @@ -114,6 +166,7 @@ describe('DashboardControls', () => { hideTimeControls: true, hideVariableControls: true, hideLinksControls: true, + hideDashboardControls: true, }); const setState = jest.spyOn(scene, 'setState'); @@ -121,6 +174,7 @@ describe('DashboardControls', () => { '_dash.hideTimePicker': 'true', '_dash.hideVariables': 'true', '_dash.hideLinks': 'true', + '_dash.hideDashboardControls': 'true', }); expect(setState).toHaveBeenCalledTimes(0); @@ -151,6 +205,19 @@ function buildTestScene(state?: Partial): DashboardContr targetBlank: false, tooltip: 'Link', }, + { + title: 'Link (dashboard controls)', + url: 'http://localhost:3000/$A', + type: 'link', + asDropdown: false, + icon: '', + includeVars: true, + keepTime: true, + tags: [], + targetBlank: false, + tooltip: 'Link', + placement: 'inControlsMenu', + }, ], $variables: new SceneVariableSet({ variables: [variable], diff --git a/public/app/features/dashboard-scene/scene/DashboardControls.tsx b/public/app/features/dashboard-scene/scene/DashboardControls.tsx index f553cdb77b3..1a22dd8a2c0 100644 --- a/public/app/features/dashboard-scene/scene/DashboardControls.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardControls.tsx @@ -31,6 +31,8 @@ export interface DashboardControlsState extends SceneObjectState { hideTimeControls?: boolean; hideVariableControls?: boolean; hideLinksControls?: boolean; + // Hides the dashbaord-controls dropdown menu + hideDashboardControls?: boolean; } export class DashboardControls extends SceneObjectBase { @@ -41,7 +43,7 @@ export class DashboardControls extends SceneObjectBase { }); protected _urlSync = new SceneObjectUrlSyncConfig(this, { - keys: ['_dash.hideTimePicker', '_dash.hideVariables', '_dash.hideLinks'], + keys: ['_dash.hideTimePicker', '_dash.hideVariables', '_dash.hideLinks', '_dash.hideDashboardControls'], }); /** @@ -53,7 +55,7 @@ export class DashboardControls extends SceneObjectBase { } updateFromUrl(values: SceneObjectUrlValues) { - const { hideTimeControls, hideVariableControls, hideLinksControls } = this.state; + const { hideTimeControls, hideVariableControls, hideLinksControls, hideDashboardControls } = this.state; const isEnabledViaUrl = (key: string) => values[key] === 'true' || values[key] === ''; // Only allow hiding, never "unhiding" from url @@ -70,6 +72,10 @@ export class DashboardControls extends SceneObjectBase { if (!hideLinksControls && isEnabledViaUrl('_dash.hideLinks')) { this.setState({ hideLinksControls: true }); } + + if (!hideDashboardControls && isEnabledViaUrl('_dash.hideDashboardControls')) { + this.setState({ hideDashboardControls: true }); + } } public constructor(state: Partial) { @@ -104,6 +110,18 @@ export class DashboardControls extends SceneObjectBase { } } + // Dashboard controls is a separate dropdown menu at the top-right of the controls + public hasDashboardControls(): boolean { + const dashboard = getDashboardSceneFor(this); + const { links } = dashboard.state; + const hasControlMenuVariables = sceneGraph + .getVariables(dashboard) + ?.state.variables.some((v) => v.state.showInControlsMenu === true); + const hasControlMenuLinks = links.some((link) => link.placement === 'inControlsMenu'); + + return hasControlMenuVariables || hasControlMenuLinks; + } + public hasControls(): boolean { const hasVariables = sceneGraph .getVariables(this) @@ -113,22 +131,25 @@ export class DashboardControls extends SceneObjectBase { const hideLinks = this.state.hideLinksControls || !hasLinks; const hideVariables = this.state.hideVariableControls || (!hasAnnotations && !hasVariables); const hideTimePicker = this.state.hideTimeControls; + const hideDashboardControls = this.state.hideDashboardControls || !this.hasDashboardControls(); - return !(hideVariables && hideLinks && hideTimePicker); + return !(hideVariables && hideLinks && hideTimePicker && hideDashboardControls); } } function DashboardControlsRenderer({ model }: SceneComponentProps) { - const { refreshPicker, timePicker, hideTimeControls, hideVariableControls, hideLinksControls } = model.useState(); + const { + refreshPicker, + timePicker, + hideTimeControls, + hideVariableControls, + hideLinksControls, + hideDashboardControls, + } = model.useState(); const dashboard = getDashboardSceneFor(model); const { links, editPanel } = dashboard.useState(); const styles = useStyles2(getStyles); const showDebugger = window.location.search.includes('scene-debugger'); - const hasControlMenuVariables = sceneGraph - .getVariables(dashboard) - .useState() - .variables.some((v) => v.state.showInControlsMenu === true); - const hasControlMenuLinks = links.some((link) => link.placement === 'inControlsMenu'); if (!model.hasControls()) { // To still have spacing when no controls are rendered @@ -157,7 +178,7 @@ function DashboardControlsRenderer({ model }: SceneComponentProps )} - {(hasControlMenuVariables || hasControlMenuLinks) && ( + {!hideDashboardControls && model.hasDashboardControls() && ( From 185e2234b52b61e97376ecb8cf25964263b9185b Mon Sep 17 00:00:00 2001 From: Ashley Harrison Date: Mon, 15 Sep 2025 13:23:10 +0100 Subject: [PATCH 33/33] Chore: Update generated scss (#111090) update generated scss --- public/sass/_variables.dark.generated.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/sass/_variables.dark.generated.scss b/public/sass/_variables.dark.generated.scss index 0226c7b617d..aba363ed213 100644 --- a/public/sass/_variables.dark.generated.scss +++ b/public/sass/_variables.dark.generated.scss @@ -55,7 +55,7 @@ $text-color-emphasis: #ffffff; // Links // ------------------------- $link-color: rgb(204, 204, 220); -$link-color-disabled: rgba(204, 204, 220, 0.6); +$link-color-disabled: rgba(204, 204, 220, 0.61); $link-hover-color: #ffffff; // Typography