Merge remote-tracking branch 'origin' into attempt_at_short_url_updates
This commit is contained in:
@@ -85,6 +85,7 @@
|
||||
# Git Sync frontend owned by frontend team as a whole.
|
||||
|
||||
/apps/alerting/ @grafana/alerting-backend
|
||||
/apps/quotas/ @grafana/grafana-search-and-storage
|
||||
/apps/dashboard/ @grafana/grafana-app-platform-squad @grafana/dashboards-squad
|
||||
/apps/folder/ @grafana/grafana-app-platform-squad
|
||||
/apps/playlist/ @grafana/grafana-app-platform-squad
|
||||
|
||||
@@ -93,6 +93,7 @@ COPY pkg/storage/unified/apistore pkg/storage/unified/apistore
|
||||
COPY pkg/semconv pkg/semconv
|
||||
COPY pkg/aggregator pkg/aggregator
|
||||
COPY apps/playlist apps/playlist
|
||||
COPY apps/quotas apps/quotas
|
||||
COPY apps/plugins apps/plugins
|
||||
COPY apps/shorturl apps/shorturl
|
||||
COPY apps/annotation apps/annotation
|
||||
|
||||
@@ -224,6 +224,8 @@ github.com/grafana/alerting v0.0.0-20251204145817-de8c2bbf9eba h1:psKWNETD5nGxmF
|
||||
github.com/grafana/alerting v0.0.0-20251204145817-de8c2bbf9eba/go.mod h1:l7v67cgP7x72ajB9UPZlumdrHqNztpKoqQ52cU8T3LU=
|
||||
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.48.5 h1:MS8l9fTZz+VbTfgApn09jw27GxhQ6fNOWGhC4ydvZmM=
|
||||
github.com/grafana/grafana-app-sdk v0.48.5/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.48.3 h1:72NUpGNiJXCNQz/on++YSsl38xuVYYBKv5kKQaOClX4=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.48.3/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
|
||||
github.com/grafana/loki/pkg/push v0.0.0-20250823105456-332df2b20000 h1:/5LKSYgLmAhwA4m6iGUD4w1YkydEWWjazn9qxCFT8W0=
|
||||
|
||||
@@ -768,6 +768,10 @@ VariableRefresh: *"never" | "onDashboardLoad" | "onTimeRangeChanged"
|
||||
// Accepted values are `dontHide` (show label and value), `hideLabel` (show value only), `hideVariable` (show nothing).
|
||||
VariableHide: *"dontHide" | "hideLabel" | "hideVariable"
|
||||
|
||||
// Determine whether regex applies to variable value or display text
|
||||
// Accepted values are `value` (apply to value used in queries) or `text` (apply to display text shown to users)
|
||||
VariableRegexApplyTo: *"value" | "text"
|
||||
|
||||
// Determine the origin of the adhoc variable filter
|
||||
FilterOrigin: "dashboard"
|
||||
|
||||
@@ -803,6 +807,7 @@ QueryVariableSpec: {
|
||||
datasource?: DataSourceRef
|
||||
query: DataQueryKind
|
||||
regex: string | *""
|
||||
regexApplyTo?: VariableRegexApplyTo
|
||||
sort: VariableSort
|
||||
definition?: string
|
||||
options: [...VariableOption] | *[]
|
||||
|
||||
@@ -772,6 +772,10 @@ VariableRefresh: *"never" | "onDashboardLoad" | "onTimeRangeChanged"
|
||||
// Accepted values are `dontHide` (show label and value), `hideLabel` (show value only), `hideVariable` (show nothing), `inControlsMenu` (show in a drop-down menu).
|
||||
VariableHide: *"dontHide" | "hideLabel" | "hideVariable" | "inControlsMenu"
|
||||
|
||||
// Determine whether regex applies to variable value or display text
|
||||
// Accepted values are `value` (apply to value used in queries) or `text` (apply to display text shown to users)
|
||||
VariableRegexApplyTo: *"value" | "text"
|
||||
|
||||
// Determine the origin of the adhoc variable filter
|
||||
FilterOrigin: "dashboard"
|
||||
|
||||
@@ -806,6 +810,7 @@ QueryVariableSpec: {
|
||||
description?: string
|
||||
query: DataQueryKind
|
||||
regex: string | *""
|
||||
regexApplyTo?: VariableRegexApplyTo
|
||||
sort: VariableSort
|
||||
definition?: string
|
||||
options: [...VariableOption] | *[]
|
||||
|
||||
@@ -222,6 +222,8 @@ lineage: schemas: [{
|
||||
// Optional field, if you want to extract part of a series name or metric node segment.
|
||||
// Named capture groups can be used to separate the display text and value.
|
||||
regex?: string
|
||||
// Determine whether regex applies to variable value or display text
|
||||
regexApplyTo?: #VariableRegexApplyTo
|
||||
// Additional static options for query variable
|
||||
staticOptions?: [...#VariableOption]
|
||||
// Ordering of static options in relation to options returned from data source for query variable
|
||||
@@ -249,6 +251,10 @@ lineage: schemas: [{
|
||||
// Accepted values are 0 (show label and value), 1 (show value only), 2 (show nothing), 3 (show under the controls dropdown menu).
|
||||
#VariableHide: 0 | 1 | 2 | 3 @cuetsy(kind="enum",memberNames="dontHide|hideLabel|hideVariable|inControlsMenu") @grafana(TSVeneer="type")
|
||||
|
||||
// Determine whether regex applies to variable value or display text
|
||||
// Accepted values are "value" (apply to value used in queries) or "text" (apply to display text shown to users)
|
||||
#VariableRegexApplyTo: "value" | "text" @cuetsy(kind="type")
|
||||
|
||||
// Sort variable options
|
||||
// Accepted values are:
|
||||
// `0`: No sorting
|
||||
|
||||
@@ -222,6 +222,8 @@ lineage: schemas: [{
|
||||
// Optional field, if you want to extract part of a series name or metric node segment.
|
||||
// Named capture groups can be used to separate the display text and value.
|
||||
regex?: string
|
||||
// Determine whether regex applies to variable value or display text
|
||||
regexApplyTo?: #VariableRegexApplyTo
|
||||
// Additional static options for query variable
|
||||
staticOptions?: [...#VariableOption]
|
||||
// Ordering of static options in relation to options returned from data source for query variable
|
||||
@@ -249,6 +251,10 @@ lineage: schemas: [{
|
||||
// Accepted values are 0 (show label and value), 1 (show value only), 2 (show nothing), 3 (show under the controls dropdown menu).
|
||||
#VariableHide: 0 | 1 | 2 | 3 @cuetsy(kind="enum",memberNames="dontHide|hideLabel|hideVariable|inControlsMenu") @grafana(TSVeneer="type")
|
||||
|
||||
// Determine whether regex applies to variable value or display text
|
||||
// Accepted values are "value" (apply to value used in queries) or "text" (apply to display text shown to users)
|
||||
#VariableRegexApplyTo: "value" | "text" @cuetsy(kind="type")
|
||||
|
||||
// Sort variable options
|
||||
// Accepted values are:
|
||||
// `0`: No sorting
|
||||
|
||||
@@ -772,6 +772,10 @@ VariableRefresh: *"never" | "onDashboardLoad" | "onTimeRangeChanged"
|
||||
// Accepted values are `dontHide` (show label and value), `hideLabel` (show value only), `hideVariable` (show nothing).
|
||||
VariableHide: *"dontHide" | "hideLabel" | "hideVariable"
|
||||
|
||||
// Determine whether regex applies to variable value or display text
|
||||
// Accepted values are `value` (apply to value used in queries) or `text` (apply to display text shown to users)
|
||||
VariableRegexApplyTo: *"value" | "text"
|
||||
|
||||
// Determine the origin of the adhoc variable filter
|
||||
FilterOrigin: "dashboard"
|
||||
|
||||
@@ -807,6 +811,7 @@ QueryVariableSpec: {
|
||||
datasource?: DataSourceRef
|
||||
query: DataQueryKind
|
||||
regex: string | *""
|
||||
regexApplyTo?: VariableRegexApplyTo
|
||||
sort: VariableSort
|
||||
definition?: string
|
||||
options: [...VariableOption] | *[]
|
||||
|
||||
@@ -1364,6 +1364,7 @@ type DashboardQueryVariableSpec struct {
|
||||
Datasource *DashboardDataSourceRef `json:"datasource,omitempty"`
|
||||
Query DashboardDataQueryKind `json:"query"`
|
||||
Regex string `json:"regex"`
|
||||
RegexApplyTo *DashboardVariableRegexApplyTo `json:"regexApplyTo,omitempty"`
|
||||
Sort DashboardVariableSort `json:"sort"`
|
||||
Definition *string `json:"definition,omitempty"`
|
||||
Options []DashboardVariableOption `json:"options"`
|
||||
@@ -1393,6 +1394,7 @@ func NewDashboardQueryVariableSpec() *DashboardQueryVariableSpec {
|
||||
SkipUrlSync: false,
|
||||
Query: *NewDashboardDataQueryKind(),
|
||||
Regex: "",
|
||||
RegexApplyTo: (func(input DashboardVariableRegexApplyTo) *DashboardVariableRegexApplyTo { return &input })(DashboardVariableRegexApplyToValue),
|
||||
Options: []DashboardVariableOption{},
|
||||
Multi: false,
|
||||
IncludeAll: false,
|
||||
@@ -1443,6 +1445,16 @@ const (
|
||||
DashboardVariableRefreshOnTimeRangeChanged DashboardVariableRefresh = "onTimeRangeChanged"
|
||||
)
|
||||
|
||||
// Determine whether regex applies to variable value or display text
|
||||
// Accepted values are `value` (apply to value used in queries) or `text` (apply to display text shown to users)
|
||||
// +k8s:openapi-gen=true
|
||||
type DashboardVariableRegexApplyTo string
|
||||
|
||||
const (
|
||||
DashboardVariableRegexApplyToValue DashboardVariableRegexApplyTo = "value"
|
||||
DashboardVariableRegexApplyToText DashboardVariableRegexApplyTo = "text"
|
||||
)
|
||||
|
||||
// Sort variable options
|
||||
// Accepted values are:
|
||||
// `disabled`: No sorting
|
||||
|
||||
@@ -3646,6 +3646,12 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardQueryVariableSpec(ref common.Re
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"regexApplyTo": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"sort": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
|
||||
@@ -776,6 +776,10 @@ VariableRefresh: *"never" | "onDashboardLoad" | "onTimeRangeChanged"
|
||||
// Accepted values are `dontHide` (show label and value), `hideLabel` (show value only), `hideVariable` (show nothing), `inControlsMenu` (show in a drop-down menu).
|
||||
VariableHide: *"dontHide" | "hideLabel" | "hideVariable" | "inControlsMenu"
|
||||
|
||||
// Determine whether regex applies to variable value or display text
|
||||
// Accepted values are `value` (apply to value used in queries) or `text` (apply to display text shown to users)
|
||||
VariableRegexApplyTo: *"value" | "text"
|
||||
|
||||
// Determine the origin of the adhoc variable filter
|
||||
FilterOrigin: "dashboard"
|
||||
|
||||
@@ -810,6 +814,7 @@ QueryVariableSpec: {
|
||||
description?: string
|
||||
query: DataQueryKind
|
||||
regex: string | *""
|
||||
regexApplyTo?: VariableRegexApplyTo
|
||||
sort: VariableSort
|
||||
definition?: string
|
||||
options: [...VariableOption] | *[]
|
||||
|
||||
@@ -1367,6 +1367,7 @@ type DashboardQueryVariableSpec struct {
|
||||
Description *string `json:"description,omitempty"`
|
||||
Query DashboardDataQueryKind `json:"query"`
|
||||
Regex string `json:"regex"`
|
||||
RegexApplyTo *DashboardVariableRegexApplyTo `json:"regexApplyTo,omitempty"`
|
||||
Sort DashboardVariableSort `json:"sort"`
|
||||
Definition *string `json:"definition,omitempty"`
|
||||
Options []DashboardVariableOption `json:"options"`
|
||||
@@ -1396,6 +1397,7 @@ func NewDashboardQueryVariableSpec() *DashboardQueryVariableSpec {
|
||||
SkipUrlSync: false,
|
||||
Query: *NewDashboardDataQueryKind(),
|
||||
Regex: "",
|
||||
RegexApplyTo: (func(input DashboardVariableRegexApplyTo) *DashboardVariableRegexApplyTo { return &input })(DashboardVariableRegexApplyToValue),
|
||||
Options: []DashboardVariableOption{},
|
||||
Multi: false,
|
||||
IncludeAll: false,
|
||||
@@ -1447,6 +1449,16 @@ const (
|
||||
DashboardVariableRefreshOnTimeRangeChanged DashboardVariableRefresh = "onTimeRangeChanged"
|
||||
)
|
||||
|
||||
// Determine whether regex applies to variable value or display text
|
||||
// Accepted values are `value` (apply to value used in queries) or `text` (apply to display text shown to users)
|
||||
// +k8s:openapi-gen=true
|
||||
type DashboardVariableRegexApplyTo string
|
||||
|
||||
const (
|
||||
DashboardVariableRegexApplyToValue DashboardVariableRegexApplyTo = "value"
|
||||
DashboardVariableRegexApplyToText DashboardVariableRegexApplyTo = "text"
|
||||
)
|
||||
|
||||
// Sort variable options
|
||||
// Accepted values are:
|
||||
// `disabled`: No sorting
|
||||
|
||||
@@ -3656,6 +3656,12 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardQueryVariableSpec(ref common.Ref
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"regexApplyTo": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"sort": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
|
||||
+2
-2
File diff suppressed because one or more lines are too long
@@ -12,13 +12,6 @@ import (
|
||||
)
|
||||
|
||||
func RegisterConversions(s *runtime.Scheme, dsIndexProvider schemaversion.DataSourceIndexProvider, leIndexProvider schemaversion.LibraryElementIndexProvider) error {
|
||||
// Wrap the provider once with 10s caching for all conversions.
|
||||
// This prevents repeated DB queries across multiple conversion calls while allowing
|
||||
// the cache to refresh periodically, making it suitable for long-lived singleton usage.
|
||||
dsIndexProvider = schemaversion.WrapIndexProviderWithCache(dsIndexProvider)
|
||||
// Wrap library element provider with caching as well
|
||||
leIndexProvider = schemaversion.WrapLibraryElementProviderWithCache(leIndexProvider)
|
||||
|
||||
// v0 conversions
|
||||
if err := s.AddConversionFunc((*dashv0.Dashboard)(nil), (*dashv1.Dashboard)(nil),
|
||||
withConversionMetrics(dashv0.APIVERSION, dashv1.APIVERSION, func(a, b interface{}, scope conversion.Scope) error {
|
||||
@@ -62,13 +55,13 @@ func RegisterConversions(s *runtime.Scheme, dsIndexProvider schemaversion.DataSo
|
||||
// v2alpha1 conversions
|
||||
if err := s.AddConversionFunc((*dashv2alpha1.Dashboard)(nil), (*dashv0.Dashboard)(nil),
|
||||
withConversionMetrics(dashv2alpha1.APIVERSION, dashv0.APIVERSION, func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_V2alpha1_to_V0(a.(*dashv2alpha1.Dashboard), b.(*dashv0.Dashboard), scope, dsIndexProvider)
|
||||
return Convert_V2alpha1_to_V0(a.(*dashv2alpha1.Dashboard), b.(*dashv0.Dashboard), scope)
|
||||
})); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddConversionFunc((*dashv2alpha1.Dashboard)(nil), (*dashv1.Dashboard)(nil),
|
||||
withConversionMetrics(dashv2alpha1.APIVERSION, dashv1.APIVERSION, func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_V2alpha1_to_V1beta1(a.(*dashv2alpha1.Dashboard), b.(*dashv1.Dashboard), scope, dsIndexProvider)
|
||||
return Convert_V2alpha1_to_V1beta1(a.(*dashv2alpha1.Dashboard), b.(*dashv1.Dashboard), scope)
|
||||
})); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+5
-3
@@ -42,7 +42,7 @@
|
||||
"regex": "",
|
||||
"skipUrlSync": false,
|
||||
"refresh": 1
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "query_var",
|
||||
"type": "query",
|
||||
@@ -81,6 +81,7 @@
|
||||
"allValue": ".*",
|
||||
"multi": true,
|
||||
"regex": "/.*9090.*/",
|
||||
"regexApplyTo": "text",
|
||||
"skipUrlSync": false,
|
||||
"refresh": 2,
|
||||
"sort": 1,
|
||||
@@ -107,7 +108,7 @@
|
||||
},
|
||||
{
|
||||
"selected": false,
|
||||
"text": "staging",
|
||||
"text": "staging",
|
||||
"value": "staging"
|
||||
},
|
||||
{
|
||||
@@ -335,6 +336,7 @@
|
||||
"allValue": "*",
|
||||
"multi": true,
|
||||
"regex": "/host[0-9]+/",
|
||||
"regexApplyTo": "value",
|
||||
"skipUrlSync": false,
|
||||
"refresh": 1,
|
||||
"sort": 2,
|
||||
@@ -354,4 +356,4 @@
|
||||
},
|
||||
"links": []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+2
@@ -94,6 +94,7 @@
|
||||
"query": "label_values(up, instance)",
|
||||
"refresh": 2,
|
||||
"regex": "/.*9090.*/",
|
||||
"regexApplyTo": "text",
|
||||
"skipUrlSync": false,
|
||||
"sort": 1,
|
||||
"tagValuesQuery": "",
|
||||
@@ -362,6 +363,7 @@
|
||||
},
|
||||
"refresh": 1,
|
||||
"regex": "/host[0-9]+/",
|
||||
"regexApplyTo": "value",
|
||||
"skipUrlSync": false,
|
||||
"sort": 2,
|
||||
"tagValuesQuery": "",
|
||||
|
||||
Vendored
+2
@@ -110,6 +110,7 @@
|
||||
}
|
||||
},
|
||||
"regex": "/.*9090.*/",
|
||||
"regexApplyTo": "text",
|
||||
"sort": "alphabeticalAsc",
|
||||
"definition": "label_values(up, instance)",
|
||||
"options": [
|
||||
@@ -401,6 +402,7 @@
|
||||
}
|
||||
},
|
||||
"regex": "/host[0-9]+/",
|
||||
"regexApplyTo": "value",
|
||||
"sort": "alphabeticalDesc",
|
||||
"definition": "terms field:@host size:100",
|
||||
"options": [],
|
||||
|
||||
Vendored
+2
@@ -111,6 +111,7 @@
|
||||
}
|
||||
},
|
||||
"regex": "/.*9090.*/",
|
||||
"regexApplyTo": "text",
|
||||
"sort": "alphabeticalAsc",
|
||||
"definition": "label_values(up, instance)",
|
||||
"options": [
|
||||
@@ -404,6 +405,7 @@
|
||||
}
|
||||
},
|
||||
"regex": "/host[0-9]+/",
|
||||
"regexApplyTo": "value",
|
||||
"sort": "alphabeticalDesc",
|
||||
"definition": "terms field:@host size:100",
|
||||
"options": [],
|
||||
|
||||
@@ -229,6 +229,16 @@ func getBoolField(m map[string]interface{}, key string, defaultValue bool) bool
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
func getUnionField[T ~string](m map[string]interface{}, key string) *T {
|
||||
if val, ok := m[key]; ok {
|
||||
if str, ok := val.(string); ok && str != "" {
|
||||
result := T(str)
|
||||
return &result
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Helper function to create int64 pointer
|
||||
func int64Ptr(i int64) *int64 {
|
||||
return &i
|
||||
@@ -1195,6 +1205,7 @@ func buildQueryVariable(ctx context.Context, varMap map[string]interface{}, comm
|
||||
Refresh: transformVariableRefreshToEnum(varMap["refresh"]),
|
||||
Sort: transformVariableSortToEnum(varMap["sort"]),
|
||||
Regex: schemaversion.GetStringValue(varMap, "regex"),
|
||||
RegexApplyTo: getUnionField[dashv2alpha1.DashboardVariableRegexApplyTo](varMap, "regexApplyTo"),
|
||||
Query: buildDataQueryKindForVariable(varMap["query"], datasourceType),
|
||||
AllowCustomValue: getBoolField(varMap, "allowCustomValue", true),
|
||||
},
|
||||
|
||||
@@ -11,10 +11,10 @@ import (
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion"
|
||||
)
|
||||
|
||||
func Convert_V2alpha1_to_V0(in *dashv2alpha1.Dashboard, out *dashv0.Dashboard, scope conversion.Scope, dsIndexProvider schemaversion.DataSourceIndexProvider) error {
|
||||
func Convert_V2alpha1_to_V0(in *dashv2alpha1.Dashboard, out *dashv0.Dashboard, scope conversion.Scope) error {
|
||||
// Convert v2alpha1 → v1beta1 first, then v1beta1 → v0
|
||||
v1beta1 := &dashv1.Dashboard{}
|
||||
if err := ConvertDashboard_V2alpha1_to_V1beta1(in, v1beta1, scope, dsIndexProvider); err != nil {
|
||||
if err := ConvertDashboard_V2alpha1_to_V1beta1(in, v1beta1, scope); err != nil {
|
||||
out.ObjectMeta = in.ObjectMeta
|
||||
out.APIVersion = dashv0.APIVERSION
|
||||
out.Kind = in.Kind
|
||||
@@ -53,13 +53,13 @@ func Convert_V2alpha1_to_V0(in *dashv2alpha1.Dashboard, out *dashv0.Dashboard, s
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert_V2alpha1_to_V1beta1(in *dashv2alpha1.Dashboard, out *dashv1.Dashboard, scope conversion.Scope, dsIndexProvider schemaversion.DataSourceIndexProvider) error {
|
||||
func Convert_V2alpha1_to_V1beta1(in *dashv2alpha1.Dashboard, out *dashv1.Dashboard, scope conversion.Scope) error {
|
||||
out.ObjectMeta = in.ObjectMeta
|
||||
out.APIVersion = dashv1.APIVERSION
|
||||
out.Kind = in.Kind
|
||||
|
||||
// Convert the spec
|
||||
if err := ConvertDashboard_V2alpha1_to_V1beta1(in, out, scope, dsIndexProvider); err != nil {
|
||||
if err := ConvertDashboard_V2alpha1_to_V1beta1(in, out, scope); err != nil {
|
||||
out.Status = dashv1.DashboardStatus{
|
||||
Conversion: &dashv1.DashboardConversionStatus{
|
||||
StoredVersion: ptr.To(dashv2alpha1.VERSION),
|
||||
@@ -179,7 +179,7 @@ func Convert_V2beta1_to_V1beta1(in *dashv2beta1.Dashboard, out *dashv1.Dashboard
|
||||
// Convert v2alpha1 → v1beta1
|
||||
// Note: ConvertDashboard_V2alpha1_to_V1beta1 will set out.ObjectMeta from v2alpha1,
|
||||
// but we've already set it from the original input, so it will be preserved
|
||||
if err := ConvertDashboard_V2alpha1_to_V1beta1(v2alpha1, out, scope, dsIndexProvider); err != nil {
|
||||
if err := ConvertDashboard_V2alpha1_to_V1beta1(v2alpha1, out, scope); err != nil {
|
||||
out.Status = dashv1.DashboardStatus{
|
||||
Conversion: &dashv1.DashboardConversionStatus{
|
||||
StoredVersion: ptr.To(dashv2beta1.VERSION),
|
||||
|
||||
@@ -39,7 +39,7 @@ func TestV2alpha1ConversionErrorHandling(t *testing.T) {
|
||||
}
|
||||
target := &dashv1.Dashboard{}
|
||||
|
||||
err := Convert_V2alpha1_to_V1beta1(source, target, nil, dsProvider)
|
||||
err := Convert_V2alpha1_to_V1beta1(source, target, nil)
|
||||
|
||||
// Convert_V2alpha1_to_V1beta1 doesn't return error, just sets status
|
||||
require.NoError(t, err, "Convert_V2alpha1_to_V1beta1 doesn't return error")
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
package conversion
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/conversion"
|
||||
|
||||
dashv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1"
|
||||
dashv2alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1"
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion"
|
||||
"k8s.io/apimachinery/pkg/conversion"
|
||||
)
|
||||
|
||||
// ConvertDashboard_V2alpha1_to_V1beta1 converts a v2alpha1 dashboard to v1beta1 format.
|
||||
@@ -16,19 +14,13 @@ import (
|
||||
// that represents the v1 dashboard JSON format.
|
||||
// The dsIndexProvider is used to resolve default datasources when queries/variables/annotations
|
||||
// don't have explicit datasource references.
|
||||
func ConvertDashboard_V2alpha1_to_V1beta1(in *dashv2alpha1.Dashboard, out *dashv1.Dashboard, scope conversion.Scope, dsIndexProvider schemaversion.DataSourceIndexProvider) error {
|
||||
func ConvertDashboard_V2alpha1_to_V1beta1(in *dashv2alpha1.Dashboard, out *dashv1.Dashboard, scope conversion.Scope) error {
|
||||
out.ObjectMeta = in.ObjectMeta
|
||||
out.APIVersion = dashv1.APIVERSION
|
||||
out.Kind = in.Kind // Preserve the Kind from input (should be "Dashboard")
|
||||
|
||||
// Get datasource index for resolving default datasources
|
||||
var dsIndex *schemaversion.DatasourceIndex
|
||||
if dsIndexProvider != nil {
|
||||
dsIndex = dsIndexProvider.Index(context.Background())
|
||||
}
|
||||
|
||||
// Convert the spec to v1beta1 unstructured format
|
||||
dashboardJSON, err := convertDashboardSpec_V2alpha1_to_V1beta1(&in.Spec, dsIndex)
|
||||
dashboardJSON, err := convertDashboardSpec_V2alpha1_to_V1beta1(&in.Spec)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to convert dashboard spec: %w", err)
|
||||
}
|
||||
@@ -39,7 +31,7 @@ func ConvertDashboard_V2alpha1_to_V1beta1(in *dashv2alpha1.Dashboard, out *dashv
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertDashboardSpec_V2alpha1_to_V1beta1(in *dashv2alpha1.DashboardSpec, dsIndex *schemaversion.DatasourceIndex) (map[string]interface{}, error) {
|
||||
func convertDashboardSpec_V2alpha1_to_V1beta1(in *dashv2alpha1.DashboardSpec) (map[string]interface{}, error) {
|
||||
dashboard := make(map[string]interface{})
|
||||
|
||||
// Convert basic fields
|
||||
@@ -75,7 +67,7 @@ func convertDashboardSpec_V2alpha1_to_V1beta1(in *dashv2alpha1.DashboardSpec, ds
|
||||
}
|
||||
|
||||
// Convert panels from elements and layout
|
||||
panels, err := convertPanelsFromElementsAndLayout(in.Elements, in.Layout, dsIndex)
|
||||
panels, err := convertPanelsFromElementsAndLayout(in.Elements, in.Layout)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert panels: %w", err)
|
||||
}
|
||||
@@ -90,7 +82,7 @@ func convertDashboardSpec_V2alpha1_to_V1beta1(in *dashv2alpha1.DashboardSpec, ds
|
||||
}
|
||||
|
||||
// Convert variables
|
||||
variables := convertVariablesToV1(in.Variables, dsIndex)
|
||||
variables := convertVariablesToV1(in.Variables)
|
||||
if len(variables) > 0 {
|
||||
dashboard["templating"] = map[string]interface{}{
|
||||
"list": variables,
|
||||
@@ -98,7 +90,7 @@ func convertDashboardSpec_V2alpha1_to_V1beta1(in *dashv2alpha1.DashboardSpec, ds
|
||||
}
|
||||
|
||||
// Convert annotations - always include even if empty to prevent DashboardModel from adding built-in
|
||||
annotations := convertAnnotationsToV1(in.Annotations, dsIndex)
|
||||
annotations := convertAnnotationsToV1(in.Annotations)
|
||||
dashboard["annotations"] = map[string]interface{}{
|
||||
"list": annotations,
|
||||
}
|
||||
@@ -236,28 +228,28 @@ func countTotalPanels(panels []interface{}) int {
|
||||
// - RowsLayout: Rows become row panels; nested structures are flattened
|
||||
// - AutoGridLayout: Calculates gridPos based on column count and row height
|
||||
// - TabsLayout: Tabs become expanded row panels; content is flattened
|
||||
func convertPanelsFromElementsAndLayout(elements map[string]dashv2alpha1.DashboardElement, layout dashv2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind, dsIndex *schemaversion.DatasourceIndex) ([]interface{}, error) {
|
||||
func convertPanelsFromElementsAndLayout(elements map[string]dashv2alpha1.DashboardElement, layout dashv2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind) ([]interface{}, error) {
|
||||
if layout.GridLayoutKind != nil {
|
||||
return convertGridLayoutToPanels(elements, layout.GridLayoutKind, dsIndex)
|
||||
return convertGridLayoutToPanels(elements, layout.GridLayoutKind)
|
||||
}
|
||||
|
||||
if layout.RowsLayoutKind != nil {
|
||||
return convertRowsLayoutToPanels(elements, layout.RowsLayoutKind, dsIndex)
|
||||
return convertRowsLayoutToPanels(elements, layout.RowsLayoutKind)
|
||||
}
|
||||
|
||||
if layout.AutoGridLayoutKind != nil {
|
||||
return convertAutoGridLayoutToPanels(elements, layout.AutoGridLayoutKind, dsIndex)
|
||||
return convertAutoGridLayoutToPanels(elements, layout.AutoGridLayoutKind)
|
||||
}
|
||||
|
||||
if layout.TabsLayoutKind != nil {
|
||||
return convertTabsLayoutToPanels(elements, layout.TabsLayoutKind, dsIndex)
|
||||
return convertTabsLayoutToPanels(elements, layout.TabsLayoutKind)
|
||||
}
|
||||
|
||||
// No layout specified, return empty panels
|
||||
return []interface{}{}, nil
|
||||
}
|
||||
|
||||
func convertGridLayoutToPanels(elements map[string]dashv2alpha1.DashboardElement, gridLayout *dashv2alpha1.DashboardGridLayoutKind, dsIndex *schemaversion.DatasourceIndex) ([]interface{}, error) {
|
||||
func convertGridLayoutToPanels(elements map[string]dashv2alpha1.DashboardElement, gridLayout *dashv2alpha1.DashboardGridLayoutKind) ([]interface{}, error) {
|
||||
panels := make([]interface{}, 0, len(gridLayout.Spec.Items))
|
||||
|
||||
for _, item := range gridLayout.Spec.Items {
|
||||
@@ -266,7 +258,7 @@ func convertGridLayoutToPanels(elements map[string]dashv2alpha1.DashboardElement
|
||||
return nil, fmt.Errorf("panel with uid %s not found in the dashboard elements", item.Spec.Element.Name)
|
||||
}
|
||||
|
||||
panel, err := convertPanelFromElement(&element, &item, dsIndex)
|
||||
panel, err := convertPanelFromElement(&element, &item)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert panel %s: %w", item.Spec.Element.Name, err)
|
||||
}
|
||||
@@ -279,21 +271,21 @@ func convertGridLayoutToPanels(elements map[string]dashv2alpha1.DashboardElement
|
||||
// convertRowsLayoutToPanels converts a RowsLayout to V1 panels.
|
||||
// All nested structures (rows within rows, tabs within rows) are flattened to the root level.
|
||||
// Each row becomes a row panel, and nested content is added sequentially after it.
|
||||
func convertRowsLayoutToPanels(elements map[string]dashv2alpha1.DashboardElement, rowsLayout *dashv2alpha1.DashboardRowsLayoutKind, dsIndex *schemaversion.DatasourceIndex) ([]interface{}, error) {
|
||||
return convertNestedLayoutToPanels(elements, rowsLayout, nil, dsIndex, 0)
|
||||
func convertRowsLayoutToPanels(elements map[string]dashv2alpha1.DashboardElement, rowsLayout *dashv2alpha1.DashboardRowsLayoutKind) ([]interface{}, error) {
|
||||
return convertNestedLayoutToPanels(elements, rowsLayout, nil, 0)
|
||||
}
|
||||
|
||||
// convertNestedLayoutToPanels handles arbitrary nesting of RowsLayout and TabsLayout.
|
||||
// It processes each row/tab in order, tracking Y position to ensure panels don't overlap.
|
||||
// The function recursively flattens nested structures to produce a flat V1 panel array.
|
||||
func convertNestedLayoutToPanels(elements map[string]dashv2alpha1.DashboardElement, rowsLayout *dashv2alpha1.DashboardRowsLayoutKind, tabsLayout *dashv2alpha1.DashboardTabsLayoutKind, dsIndex *schemaversion.DatasourceIndex, yOffset int64) ([]interface{}, error) {
|
||||
func convertNestedLayoutToPanels(elements map[string]dashv2alpha1.DashboardElement, rowsLayout *dashv2alpha1.DashboardRowsLayoutKind, tabsLayout *dashv2alpha1.DashboardTabsLayoutKind, yOffset int64) ([]interface{}, error) {
|
||||
panels := make([]interface{}, 0)
|
||||
currentY := yOffset
|
||||
|
||||
// Process RowsLayout
|
||||
if rowsLayout != nil {
|
||||
for _, row := range rowsLayout.Spec.Rows {
|
||||
rowPanels, newY, err := processRowItem(elements, &row, dsIndex, currentY)
|
||||
rowPanels, newY, err := processRowItem(elements, &row, currentY)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -305,7 +297,7 @@ func convertNestedLayoutToPanels(elements map[string]dashv2alpha1.DashboardEleme
|
||||
// Process TabsLayout (tabs are converted to rows)
|
||||
if tabsLayout != nil {
|
||||
for _, tab := range tabsLayout.Spec.Tabs {
|
||||
tabPanels, newY, err := processTabItem(elements, &tab, dsIndex, currentY)
|
||||
tabPanels, newY, err := processTabItem(elements, &tab, currentY)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -324,7 +316,7 @@ func convertNestedLayoutToPanels(elements map[string]dashv2alpha1.DashboardEleme
|
||||
// - Collapsed row: Panels stored inside row.panels with absolute Y positions
|
||||
// - Expanded row: Panels added to top level after the row panel
|
||||
// - Nested layouts: Parent row is preserved; nested content is flattened after it
|
||||
func processRowItem(elements map[string]dashv2alpha1.DashboardElement, row *dashv2alpha1.DashboardRowsLayoutRowKind, dsIndex *schemaversion.DatasourceIndex, startY int64) ([]interface{}, int64, error) {
|
||||
func processRowItem(elements map[string]dashv2alpha1.DashboardElement, row *dashv2alpha1.DashboardRowsLayoutRowKind, startY int64) ([]interface{}, int64, error) {
|
||||
panels := make([]interface{}, 0)
|
||||
currentY := startY
|
||||
|
||||
@@ -354,7 +346,7 @@ func processRowItem(elements map[string]dashv2alpha1.DashboardElement, row *dash
|
||||
}
|
||||
|
||||
// Then process nested rows
|
||||
nestedPanels, err := convertNestedLayoutToPanels(elements, row.Spec.Layout.RowsLayoutKind, nil, dsIndex, currentY)
|
||||
nestedPanels, err := convertNestedLayoutToPanels(elements, row.Spec.Layout.RowsLayoutKind, nil, currentY)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -387,7 +379,7 @@ func processRowItem(elements map[string]dashv2alpha1.DashboardElement, row *dash
|
||||
}
|
||||
|
||||
// Then process nested tabs
|
||||
nestedPanels, err := convertNestedLayoutToPanels(elements, nil, row.Spec.Layout.TabsLayoutKind, dsIndex, currentY)
|
||||
nestedPanels, err := convertNestedLayoutToPanels(elements, nil, row.Spec.Layout.TabsLayoutKind, currentY)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -429,7 +421,7 @@ func processRowItem(elements map[string]dashv2alpha1.DashboardElement, row *dash
|
||||
|
||||
// Add collapsed panels if row is collapsed (panels use absolute Y positions)
|
||||
if isCollapsed {
|
||||
collapsedPanels, err := extractCollapsedPanelsWithAbsoluteY(elements, &row.Spec.Layout, dsIndex, currentY+1)
|
||||
collapsedPanels, err := extractCollapsedPanelsWithAbsoluteY(elements, &row.Spec.Layout, currentY+1)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -444,7 +436,7 @@ func processRowItem(elements map[string]dashv2alpha1.DashboardElement, row *dash
|
||||
|
||||
// Add panels from row layout (only for expanded rows or hidden header rows)
|
||||
if !isCollapsed || isHiddenHeader {
|
||||
rowPanels, newY, err := extractExpandedPanels(elements, &row.Spec.Layout, dsIndex, currentY, isHiddenHeader, startY)
|
||||
rowPanels, newY, err := extractExpandedPanels(elements, &row.Spec.Layout, currentY, isHiddenHeader, startY)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -459,7 +451,7 @@ func processRowItem(elements map[string]dashv2alpha1.DashboardElement, row *dash
|
||||
// Each tab becomes an expanded row panel (collapsed=false) with an empty panels array.
|
||||
// The tab's content is flattened and added to the top level after the row panel.
|
||||
// Nested layouts within the tab are recursively processed.
|
||||
func processTabItem(elements map[string]dashv2alpha1.DashboardElement, tab *dashv2alpha1.DashboardTabsLayoutTabKind, dsIndex *schemaversion.DatasourceIndex, startY int64) ([]interface{}, int64, error) {
|
||||
func processTabItem(elements map[string]dashv2alpha1.DashboardElement, tab *dashv2alpha1.DashboardTabsLayoutTabKind, startY int64) ([]interface{}, int64, error) {
|
||||
panels := make([]interface{}, 0)
|
||||
currentY := startY
|
||||
|
||||
@@ -487,7 +479,7 @@ func processTabItem(elements map[string]dashv2alpha1.DashboardElement, tab *dash
|
||||
// Handle nested layouts inside the tab
|
||||
if tab.Spec.Layout.RowsLayoutKind != nil {
|
||||
// Nested RowsLayout inside tab
|
||||
nestedPanels, err := convertNestedLayoutToPanels(elements, tab.Spec.Layout.RowsLayoutKind, nil, dsIndex, currentY)
|
||||
nestedPanels, err := convertNestedLayoutToPanels(elements, tab.Spec.Layout.RowsLayoutKind, nil, currentY)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -495,7 +487,7 @@ func processTabItem(elements map[string]dashv2alpha1.DashboardElement, tab *dash
|
||||
currentY = getMaxYFromPanels(nestedPanels, currentY)
|
||||
} else if tab.Spec.Layout.TabsLayoutKind != nil {
|
||||
// Nested TabsLayout inside tab
|
||||
nestedPanels, err := convertNestedLayoutToPanels(elements, nil, tab.Spec.Layout.TabsLayoutKind, dsIndex, currentY)
|
||||
nestedPanels, err := convertNestedLayoutToPanels(elements, nil, tab.Spec.Layout.TabsLayoutKind, currentY)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -512,7 +504,7 @@ func processTabItem(elements map[string]dashv2alpha1.DashboardElement, tab *dash
|
||||
adjustedItem := item
|
||||
adjustedItem.Spec.Y = item.Spec.Y + currentY
|
||||
|
||||
panel, err := convertPanelFromElement(&element, &adjustedItem, dsIndex)
|
||||
panel, err := convertPanelFromElement(&element, &adjustedItem)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("failed to convert panel %s: %w", item.Spec.Element.Name, err)
|
||||
}
|
||||
@@ -525,7 +517,7 @@ func processTabItem(elements map[string]dashv2alpha1.DashboardElement, tab *dash
|
||||
}
|
||||
} else if tab.Spec.Layout.AutoGridLayoutKind != nil {
|
||||
// AutoGridLayout inside tab - convert with Y offset
|
||||
autoGridPanels, err := convertAutoGridLayoutToPanelsWithOffset(elements, tab.Spec.Layout.AutoGridLayoutKind, dsIndex, currentY)
|
||||
autoGridPanels, err := convertAutoGridLayoutToPanelsWithOffset(elements, tab.Spec.Layout.AutoGridLayoutKind, currentY)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -540,7 +532,7 @@ func processTabItem(elements map[string]dashv2alpha1.DashboardElement, tab *dash
|
||||
// Panels are positioned with absolute Y coordinates (baseY + relative Y).
|
||||
// This matches V1 behavior where collapsed row panels store their children
|
||||
// with Y positions as if the row were expanded at that location.
|
||||
func extractCollapsedPanelsWithAbsoluteY(elements map[string]dashv2alpha1.DashboardElement, layout *dashv2alpha1.DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind, dsIndex *schemaversion.DatasourceIndex, baseY int64) ([]interface{}, error) {
|
||||
func extractCollapsedPanelsWithAbsoluteY(elements map[string]dashv2alpha1.DashboardElement, layout *dashv2alpha1.DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind, baseY int64) ([]interface{}, error) {
|
||||
panels := make([]interface{}, 0)
|
||||
|
||||
if layout.GridLayoutKind != nil {
|
||||
@@ -552,7 +544,7 @@ func extractCollapsedPanelsWithAbsoluteY(elements map[string]dashv2alpha1.Dashbo
|
||||
// Create a copy with adjusted Y position
|
||||
adjustedItem := item
|
||||
adjustedItem.Spec.Y = item.Spec.Y + baseY
|
||||
panel, err := convertPanelFromElement(&element, &adjustedItem, dsIndex)
|
||||
panel, err := convertPanelFromElement(&element, &adjustedItem)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert panel %s: %w", item.Spec.Element.Name, err)
|
||||
}
|
||||
@@ -561,7 +553,7 @@ func extractCollapsedPanelsWithAbsoluteY(elements map[string]dashv2alpha1.Dashbo
|
||||
}
|
||||
// Handle AutoGridLayout for collapsed rows with Y offset
|
||||
if layout.AutoGridLayoutKind != nil {
|
||||
autoGridPanels, err := convertAutoGridLayoutToPanelsWithOffset(elements, layout.AutoGridLayoutKind, dsIndex, baseY)
|
||||
autoGridPanels, err := convertAutoGridLayoutToPanelsWithOffset(elements, layout.AutoGridLayoutKind, baseY)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -571,7 +563,7 @@ func extractCollapsedPanelsWithAbsoluteY(elements map[string]dashv2alpha1.Dashbo
|
||||
if layout.RowsLayoutKind != nil {
|
||||
currentY := baseY
|
||||
for _, row := range layout.RowsLayoutKind.Spec.Rows {
|
||||
nestedPanels, err := extractCollapsedPanelsWithAbsoluteY(elements, &row.Spec.Layout, dsIndex, currentY)
|
||||
nestedPanels, err := extractCollapsedPanelsWithAbsoluteY(elements, &row.Spec.Layout, currentY)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -582,7 +574,7 @@ func extractCollapsedPanelsWithAbsoluteY(elements map[string]dashv2alpha1.Dashbo
|
||||
if layout.TabsLayoutKind != nil {
|
||||
currentY := baseY
|
||||
for _, tab := range layout.TabsLayoutKind.Spec.Tabs {
|
||||
nestedPanels, err := extractCollapsedPanelsFromTabLayoutWithAbsoluteY(elements, &tab.Spec.Layout, dsIndex, currentY)
|
||||
nestedPanels, err := extractCollapsedPanelsFromTabLayoutWithAbsoluteY(elements, &tab.Spec.Layout, currentY)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -596,7 +588,7 @@ func extractCollapsedPanelsWithAbsoluteY(elements map[string]dashv2alpha1.Dashbo
|
||||
|
||||
// extractCollapsedPanelsFromTabLayoutWithAbsoluteY extracts panels from a tab layout with absolute Y.
|
||||
// Similar to extractCollapsedPanelsWithAbsoluteY but handles the tab-specific layout type.
|
||||
func extractCollapsedPanelsFromTabLayoutWithAbsoluteY(elements map[string]dashv2alpha1.DashboardElement, layout *dashv2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind, dsIndex *schemaversion.DatasourceIndex, baseY int64) ([]interface{}, error) {
|
||||
func extractCollapsedPanelsFromTabLayoutWithAbsoluteY(elements map[string]dashv2alpha1.DashboardElement, layout *dashv2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind, baseY int64) ([]interface{}, error) {
|
||||
panels := make([]interface{}, 0)
|
||||
|
||||
if layout.GridLayoutKind != nil {
|
||||
@@ -607,7 +599,7 @@ func extractCollapsedPanelsFromTabLayoutWithAbsoluteY(elements map[string]dashv2
|
||||
}
|
||||
adjustedItem := item
|
||||
adjustedItem.Spec.Y = item.Spec.Y + baseY
|
||||
panel, err := convertPanelFromElement(&element, &adjustedItem, dsIndex)
|
||||
panel, err := convertPanelFromElement(&element, &adjustedItem)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert panel %s: %w", item.Spec.Element.Name, err)
|
||||
}
|
||||
@@ -615,7 +607,7 @@ func extractCollapsedPanelsFromTabLayoutWithAbsoluteY(elements map[string]dashv2
|
||||
}
|
||||
}
|
||||
if layout.AutoGridLayoutKind != nil {
|
||||
autoGridPanels, err := convertAutoGridLayoutToPanelsWithOffset(elements, layout.AutoGridLayoutKind, dsIndex, baseY)
|
||||
autoGridPanels, err := convertAutoGridLayoutToPanelsWithOffset(elements, layout.AutoGridLayoutKind, baseY)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -624,7 +616,7 @@ func extractCollapsedPanelsFromTabLayoutWithAbsoluteY(elements map[string]dashv2
|
||||
if layout.RowsLayoutKind != nil {
|
||||
currentY := baseY
|
||||
for _, row := range layout.RowsLayoutKind.Spec.Rows {
|
||||
nestedPanels, err := extractCollapsedPanelsWithAbsoluteY(elements, &row.Spec.Layout, dsIndex, currentY)
|
||||
nestedPanels, err := extractCollapsedPanelsWithAbsoluteY(elements, &row.Spec.Layout, currentY)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -635,7 +627,7 @@ func extractCollapsedPanelsFromTabLayoutWithAbsoluteY(elements map[string]dashv2
|
||||
if layout.TabsLayoutKind != nil {
|
||||
currentY := baseY
|
||||
for _, tab := range layout.TabsLayoutKind.Spec.Tabs {
|
||||
nestedPanels, err := extractCollapsedPanelsFromTabLayoutWithAbsoluteY(elements, &tab.Spec.Layout, dsIndex, currentY)
|
||||
nestedPanels, err := extractCollapsedPanelsFromTabLayoutWithAbsoluteY(elements, &tab.Spec.Layout, currentY)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -679,7 +671,7 @@ func getLayoutHeightFromTab(layout *dashv2alpha1.DashboardGridLayoutKindOrRowsLa
|
||||
// - Explicit row: Add (currentY - 1) to relative Y for absolute positioning
|
||||
//
|
||||
// Returns the panels and the new Y position for the next row.
|
||||
func extractExpandedPanels(elements map[string]dashv2alpha1.DashboardElement, layout *dashv2alpha1.DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind, dsIndex *schemaversion.DatasourceIndex, currentY int64, isHiddenHeader bool, startY int64) ([]interface{}, int64, error) {
|
||||
func extractExpandedPanels(elements map[string]dashv2alpha1.DashboardElement, layout *dashv2alpha1.DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind, currentY int64, isHiddenHeader bool, startY int64) ([]interface{}, int64, error) {
|
||||
panels := make([]interface{}, 0)
|
||||
// For hidden headers, don't track Y changes (matches original behavior)
|
||||
maxY := startY
|
||||
@@ -700,7 +692,7 @@ func extractExpandedPanels(elements map[string]dashv2alpha1.DashboardElement, la
|
||||
}
|
||||
// For hidden headers: don't adjust Y, keep item.Spec.Y as-is
|
||||
|
||||
panel, err := convertPanelFromElement(&element, &adjustedItem, dsIndex)
|
||||
panel, err := convertPanelFromElement(&element, &adjustedItem)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("failed to convert panel %s: %w", item.Spec.Element.Name, err)
|
||||
}
|
||||
@@ -725,7 +717,7 @@ func extractExpandedPanels(elements map[string]dashv2alpha1.DashboardElement, la
|
||||
yOffset = currentY - 1
|
||||
}
|
||||
|
||||
autoGridPanels, err := convertAutoGridLayoutToPanelsWithOffset(elements, layout.AutoGridLayoutKind, dsIndex, yOffset)
|
||||
autoGridPanels, err := convertAutoGridLayoutToPanelsWithOffset(elements, layout.AutoGridLayoutKind, yOffset)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -788,7 +780,7 @@ func getLayoutHeight(layout *dashv2alpha1.DashboardGridLayoutKindOrAutoGridLayou
|
||||
// convertAutoGridLayoutToPanelsWithOffset converts AutoGridLayout with a Y offset.
|
||||
// Same as convertAutoGridLayoutToPanels but starts at yOffset instead of 0.
|
||||
// Used when AutoGridLayout appears inside rows or tabs.
|
||||
func convertAutoGridLayoutToPanelsWithOffset(elements map[string]dashv2alpha1.DashboardElement, autoGridLayout *dashv2alpha1.DashboardAutoGridLayoutKind, dsIndex *schemaversion.DatasourceIndex, yOffset int64) ([]interface{}, error) {
|
||||
func convertAutoGridLayoutToPanelsWithOffset(elements map[string]dashv2alpha1.DashboardElement, autoGridLayout *dashv2alpha1.DashboardAutoGridLayoutKind, yOffset int64) ([]interface{}, error) {
|
||||
panels := make([]interface{}, 0, len(autoGridLayout.Spec.Items))
|
||||
|
||||
const (
|
||||
@@ -850,7 +842,7 @@ func convertAutoGridLayoutToPanelsWithOffset(elements map[string]dashv2alpha1.Da
|
||||
},
|
||||
}
|
||||
|
||||
panel, err := convertPanelFromElement(&element, &gridItem, dsIndex)
|
||||
panel, err := convertPanelFromElement(&element, &gridItem)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert panel %s: %w", item.Spec.Element.Name, err)
|
||||
}
|
||||
@@ -876,7 +868,7 @@ func convertAutoGridLayoutToPanelsWithOffset(elements map[string]dashv2alpha1.Da
|
||||
//
|
||||
// Width: 24 / maxColumnCount (default 3 columns = 8 units wide)
|
||||
// Height: Predefined grid units per mode (see pixelsToGridUnits for custom)
|
||||
func convertAutoGridLayoutToPanels(elements map[string]dashv2alpha1.DashboardElement, autoGridLayout *dashv2alpha1.DashboardAutoGridLayoutKind, dsIndex *schemaversion.DatasourceIndex) ([]interface{}, error) {
|
||||
func convertAutoGridLayoutToPanels(elements map[string]dashv2alpha1.DashboardElement, autoGridLayout *dashv2alpha1.DashboardAutoGridLayoutKind) ([]interface{}, error) {
|
||||
panels := make([]interface{}, 0, len(autoGridLayout.Spec.Items))
|
||||
|
||||
const (
|
||||
@@ -963,7 +955,7 @@ func convertAutoGridLayoutToPanels(elements map[string]dashv2alpha1.DashboardEle
|
||||
}
|
||||
}
|
||||
|
||||
panel, err := convertPanelFromElement(&element, &gridItem, dsIndex)
|
||||
panel, err := convertPanelFromElement(&element, &gridItem)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert panel %s: %w", item.Spec.Element.Name, err)
|
||||
}
|
||||
@@ -984,11 +976,11 @@ func convertAutoGridLayoutToPanels(elements map[string]dashv2alpha1.DashboardEle
|
||||
// V1 has no native tab concept, so tabs are converted to expanded row panels.
|
||||
// Each tab becomes a row panel (collapsed=false, panels=[]) with its content
|
||||
// flattened to the top level. Tab order is preserved in the output.
|
||||
func convertTabsLayoutToPanels(elements map[string]dashv2alpha1.DashboardElement, tabsLayout *dashv2alpha1.DashboardTabsLayoutKind, dsIndex *schemaversion.DatasourceIndex) ([]interface{}, error) {
|
||||
return convertNestedLayoutToPanels(elements, nil, tabsLayout, dsIndex, 0)
|
||||
func convertTabsLayoutToPanels(elements map[string]dashv2alpha1.DashboardElement, tabsLayout *dashv2alpha1.DashboardTabsLayoutKind) ([]interface{}, error) {
|
||||
return convertNestedLayoutToPanels(elements, nil, tabsLayout, 0)
|
||||
}
|
||||
|
||||
func convertPanelFromElement(element *dashv2alpha1.DashboardElement, layoutItem *dashv2alpha1.DashboardGridLayoutItemKind, dsIndex *schemaversion.DatasourceIndex) (map[string]interface{}, error) {
|
||||
func convertPanelFromElement(element *dashv2alpha1.DashboardElement, layoutItem *dashv2alpha1.DashboardGridLayoutItemKind) (map[string]interface{}, error) {
|
||||
panel := make(map[string]interface{})
|
||||
|
||||
// Set grid position
|
||||
@@ -1017,7 +1009,7 @@ func convertPanelFromElement(element *dashv2alpha1.DashboardElement, layoutItem
|
||||
}
|
||||
|
||||
if element.PanelKind != nil {
|
||||
return convertPanelKindToV1(element.PanelKind, panel, dsIndex)
|
||||
return convertPanelKindToV1(element.PanelKind, panel)
|
||||
}
|
||||
|
||||
if element.LibraryPanelKind != nil {
|
||||
@@ -1027,7 +1019,7 @@ func convertPanelFromElement(element *dashv2alpha1.DashboardElement, layoutItem
|
||||
return nil, fmt.Errorf("element has neither PanelKind nor LibraryPanelKind")
|
||||
}
|
||||
|
||||
func convertPanelKindToV1(panelKind *dashv2alpha1.DashboardPanelKind, panel map[string]interface{}, dsIndex *schemaversion.DatasourceIndex) (map[string]interface{}, error) {
|
||||
func convertPanelKindToV1(panelKind *dashv2alpha1.DashboardPanelKind, panel map[string]interface{}) (map[string]interface{}, error) {
|
||||
spec := panelKind.Spec
|
||||
|
||||
panel["id"] = int(spec.Id)
|
||||
@@ -1069,14 +1061,14 @@ func convertPanelKindToV1(panelKind *dashv2alpha1.DashboardPanelKind, panel map[
|
||||
// Convert queries (targets)
|
||||
targets := make([]map[string]interface{}, 0, len(spec.Data.Spec.Queries))
|
||||
for _, query := range spec.Data.Spec.Queries {
|
||||
target := convertPanelQueryToV1(&query, dsIndex)
|
||||
target := convertPanelQueryToV1(&query)
|
||||
targets = append(targets, target)
|
||||
}
|
||||
panel["targets"] = targets
|
||||
|
||||
// Detect mixed datasource - set panel.datasource to "mixed" if queries use different datasources
|
||||
// This matches the frontend behavior in getPanelDataSource (layoutSerializers/utils.ts)
|
||||
if mixedDS := detectMixedDatasource(spec.Data.Spec.Queries, dsIndex); mixedDS != nil {
|
||||
if mixedDS := detectMixedDatasource(spec.Data.Spec.Queries); mixedDS != nil {
|
||||
panel["datasource"] = mixedDS
|
||||
}
|
||||
|
||||
@@ -1125,7 +1117,7 @@ func convertPanelKindToV1(panelKind *dashv2alpha1.DashboardPanelKind, panel map[
|
||||
return panel, nil
|
||||
}
|
||||
|
||||
func convertPanelQueryToV1(query *dashv2alpha1.DashboardPanelQueryKind, dsIndex *schemaversion.DatasourceIndex) map[string]interface{} {
|
||||
func convertPanelQueryToV1(query *dashv2alpha1.DashboardPanelQueryKind) map[string]interface{} {
|
||||
target := make(map[string]interface{})
|
||||
|
||||
// Copy query spec (excluding refId, hide, datasource which are handled separately)
|
||||
@@ -1150,7 +1142,7 @@ func convertPanelQueryToV1(query *dashv2alpha1.DashboardPanelQueryKind, dsIndex
|
||||
}
|
||||
|
||||
// Resolve datasource based on V2 input (reuse shared function)
|
||||
datasource := getDataSourceForQuery(query.Spec.Datasource, query.Spec.Query.Kind, nil)
|
||||
datasource := getDataSourceForQuery(query.Spec.Datasource, query.Spec.Query.Kind)
|
||||
if datasource != nil {
|
||||
target["datasource"] = datasource
|
||||
}
|
||||
@@ -1164,7 +1156,7 @@ func convertPanelQueryToV1(query *dashv2alpha1.DashboardPanelQueryKind, dsIndex
|
||||
// - Else if queryKind (type) is non-empty → return {type} only
|
||||
// - Else → return nil (no datasource)
|
||||
// Used for variables and annotations. Panel queries use convertPanelQueryToV1Target.
|
||||
func getDataSourceForQuery(explicitDS *dashv2alpha1.DashboardDataSourceRef, queryKind string, _ *schemaversion.DatasourceIndex) map[string]interface{} {
|
||||
func getDataSourceForQuery(explicitDS *dashv2alpha1.DashboardDataSourceRef, queryKind string) map[string]interface{} {
|
||||
// Case 1: Explicit datasource with UID provided
|
||||
if explicitDS != nil && explicitDS.Uid != nil && *explicitDS.Uid != "" {
|
||||
datasource := map[string]interface{}{
|
||||
@@ -1195,7 +1187,7 @@ func getDataSourceForQuery(explicitDS *dashv2alpha1.DashboardDataSourceRef, quer
|
||||
// Compares based on V2 input without runtime resolution:
|
||||
// - If query has explicit datasource.uid → use that UID and type
|
||||
// - Else → use query.Kind as type (empty UID)
|
||||
func detectMixedDatasource(queries []dashv2alpha1.DashboardPanelQueryKind, _ *schemaversion.DatasourceIndex) map[string]interface{} {
|
||||
func detectMixedDatasource(queries []dashv2alpha1.DashboardPanelQueryKind) map[string]interface{} {
|
||||
if len(queries) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -1254,7 +1246,7 @@ func convertLibraryPanelKindToV1(libPanelKind *dashv2alpha1.DashboardLibraryPane
|
||||
return panel, nil
|
||||
}
|
||||
|
||||
func convertVariablesToV1(variables []dashv2alpha1.DashboardVariableKind, dsIndex *schemaversion.DatasourceIndex) []map[string]interface{} {
|
||||
func convertVariablesToV1(variables []dashv2alpha1.DashboardVariableKind) []map[string]interface{} {
|
||||
result := make([]map[string]interface{}, 0, len(variables))
|
||||
|
||||
for _, variable := range variables {
|
||||
@@ -1262,7 +1254,7 @@ func convertVariablesToV1(variables []dashv2alpha1.DashboardVariableKind, dsInde
|
||||
var err error
|
||||
|
||||
if variable.QueryVariableKind != nil {
|
||||
varMap, err = convertQueryVariableToV1(variable.QueryVariableKind, dsIndex)
|
||||
varMap, err = convertQueryVariableToV1(variable.QueryVariableKind)
|
||||
} else if variable.DatasourceVariableKind != nil {
|
||||
varMap, err = convertDatasourceVariableToV1(variable.DatasourceVariableKind)
|
||||
} else if variable.CustomVariableKind != nil {
|
||||
@@ -1274,9 +1266,9 @@ func convertVariablesToV1(variables []dashv2alpha1.DashboardVariableKind, dsInde
|
||||
} else if variable.TextVariableKind != nil {
|
||||
varMap, err = convertTextVariableToV1(variable.TextVariableKind)
|
||||
} else if variable.GroupByVariableKind != nil {
|
||||
varMap, err = convertGroupByVariableToV1(variable.GroupByVariableKind, dsIndex)
|
||||
varMap, err = convertGroupByVariableToV1(variable.GroupByVariableKind)
|
||||
} else if variable.AdhocVariableKind != nil {
|
||||
varMap, err = convertAdhocVariableToV1(variable.AdhocVariableKind, dsIndex)
|
||||
varMap, err = convertAdhocVariableToV1(variable.AdhocVariableKind)
|
||||
} else if variable.SwitchVariableKind != nil {
|
||||
varMap, err = convertSwitchVariableToV1(variable.SwitchVariableKind)
|
||||
}
|
||||
@@ -1289,7 +1281,7 @@ func convertVariablesToV1(variables []dashv2alpha1.DashboardVariableKind, dsInde
|
||||
return result
|
||||
}
|
||||
|
||||
func convertQueryVariableToV1(variable *dashv2alpha1.DashboardQueryVariableKind, dsIndex *schemaversion.DatasourceIndex) (map[string]interface{}, error) {
|
||||
func convertQueryVariableToV1(variable *dashv2alpha1.DashboardQueryVariableKind) (map[string]interface{}, error) {
|
||||
spec := variable.Spec
|
||||
varMap := map[string]interface{}{
|
||||
"name": spec.Name,
|
||||
@@ -1320,6 +1312,9 @@ func convertQueryVariableToV1(variable *dashv2alpha1.DashboardQueryVariableKind,
|
||||
if spec.Definition != nil {
|
||||
varMap["definition"] = *spec.Definition
|
||||
}
|
||||
if spec.RegexApplyTo != nil {
|
||||
varMap["regexApplyTo"] = string(*spec.RegexApplyTo)
|
||||
}
|
||||
varMap["allowCustomValue"] = spec.AllowCustomValue
|
||||
|
||||
// Convert query - handle LEGACY_STRING_VALUE_KEY
|
||||
@@ -1336,7 +1331,7 @@ func convertQueryVariableToV1(variable *dashv2alpha1.DashboardQueryVariableKind,
|
||||
}
|
||||
|
||||
// Resolve datasource - use explicit datasource or resolve from query kind (datasource type)/default
|
||||
datasource := getDataSourceForQuery(spec.Datasource, spec.Query.Kind, dsIndex)
|
||||
datasource := getDataSourceForQuery(spec.Datasource, spec.Query.Kind)
|
||||
if datasource != nil {
|
||||
varMap["datasource"] = datasource
|
||||
}
|
||||
@@ -1486,7 +1481,7 @@ func convertTextVariableToV1(variable *dashv2alpha1.DashboardTextVariableKind) (
|
||||
return varMap, nil
|
||||
}
|
||||
|
||||
func convertGroupByVariableToV1(variable *dashv2alpha1.DashboardGroupByVariableKind, dsIndex *schemaversion.DatasourceIndex) (map[string]interface{}, error) {
|
||||
func convertGroupByVariableToV1(variable *dashv2alpha1.DashboardGroupByVariableKind) (map[string]interface{}, error) {
|
||||
spec := variable.Spec
|
||||
varMap := map[string]interface{}{
|
||||
"name": spec.Name,
|
||||
@@ -1509,7 +1504,7 @@ func convertGroupByVariableToV1(variable *dashv2alpha1.DashboardGroupByVariableK
|
||||
}
|
||||
|
||||
// Resolve datasource - GroupBy variables don't have a query kind, so use empty string (will fall back to default)
|
||||
datasource := getDataSourceForQuery(spec.Datasource, "", dsIndex)
|
||||
datasource := getDataSourceForQuery(spec.Datasource, "")
|
||||
if datasource != nil {
|
||||
varMap["datasource"] = datasource
|
||||
}
|
||||
@@ -1517,7 +1512,7 @@ func convertGroupByVariableToV1(variable *dashv2alpha1.DashboardGroupByVariableK
|
||||
return varMap, nil
|
||||
}
|
||||
|
||||
func convertAdhocVariableToV1(variable *dashv2alpha1.DashboardAdhocVariableKind, dsIndex *schemaversion.DatasourceIndex) (map[string]interface{}, error) {
|
||||
func convertAdhocVariableToV1(variable *dashv2alpha1.DashboardAdhocVariableKind) (map[string]interface{}, error) {
|
||||
spec := variable.Spec
|
||||
varMap := map[string]interface{}{
|
||||
"name": spec.Name,
|
||||
@@ -1536,7 +1531,7 @@ func convertAdhocVariableToV1(variable *dashv2alpha1.DashboardAdhocVariableKind,
|
||||
varMap["allowCustomValue"] = spec.AllowCustomValue
|
||||
|
||||
// Resolve datasource - Adhoc variables don't have a query kind, so use empty string (will fall back to default)
|
||||
datasource := getDataSourceForQuery(spec.Datasource, "", dsIndex)
|
||||
datasource := getDataSourceForQuery(spec.Datasource, "")
|
||||
if datasource != nil {
|
||||
varMap["datasource"] = datasource
|
||||
}
|
||||
@@ -1663,7 +1658,7 @@ func convertSwitchVariableToV1(variable *dashv2alpha1.DashboardSwitchVariableKin
|
||||
return varMap, nil
|
||||
}
|
||||
|
||||
func convertAnnotationsToV1(annotations []dashv2alpha1.DashboardAnnotationQueryKind, dsIndex *schemaversion.DatasourceIndex) []map[string]interface{} {
|
||||
func convertAnnotationsToV1(annotations []dashv2alpha1.DashboardAnnotationQueryKind) []map[string]interface{} {
|
||||
result := make([]map[string]interface{}, 0, len(annotations))
|
||||
|
||||
for _, annotation := range annotations {
|
||||
@@ -1686,7 +1681,7 @@ func convertAnnotationsToV1(annotations []dashv2alpha1.DashboardAnnotationQueryK
|
||||
if annotation.Spec.Query != nil {
|
||||
queryKind = annotation.Spec.Query.Kind
|
||||
}
|
||||
datasource := getDataSourceForQuery(annotation.Spec.Datasource, queryKind, dsIndex)
|
||||
datasource := getDataSourceForQuery(annotation.Spec.Datasource, queryKind)
|
||||
if datasource != nil {
|
||||
annotationMap["datasource"] = datasource
|
||||
}
|
||||
|
||||
@@ -767,6 +767,7 @@ func convertQueryVariableSpec_V2alpha1_to_V2beta1(in *dashv2alpha1.DashboardQuer
|
||||
out.SkipUrlSync = in.SkipUrlSync
|
||||
out.Description = in.Description
|
||||
out.Regex = in.Regex
|
||||
out.RegexApplyTo = (*dashv2beta1.DashboardVariableRegexApplyTo)(in.RegexApplyTo)
|
||||
out.Sort = dashv2beta1.DashboardVariableSort(in.Sort)
|
||||
out.Definition = in.Definition
|
||||
out.Options = convertVariableOptions_V2alpha1_to_V2beta1(in.Options)
|
||||
|
||||
@@ -806,6 +806,7 @@ func convertQueryVariableSpec_V2beta1_to_V2alpha1(in *dashv2beta1.DashboardQuery
|
||||
out.SkipUrlSync = in.SkipUrlSync
|
||||
out.Description = in.Description
|
||||
out.Regex = in.Regex
|
||||
out.RegexApplyTo = (*dashv2alpha1.DashboardVariableRegexApplyTo)(in.RegexApplyTo)
|
||||
out.Sort = dashv2alpha1.DashboardVariableSort(in.Sort)
|
||||
out.Definition = in.Definition
|
||||
out.Options = convertVariableOptions_V2beta1_to_V2alpha1(in.Options)
|
||||
|
||||
@@ -61,9 +61,13 @@ type migrator struct {
|
||||
|
||||
func (m *migrator) init(dsIndexProvider schemaversion.DataSourceIndexProvider, leIndexProvider schemaversion.LibraryElementIndexProvider) {
|
||||
initOnce.Do(func() {
|
||||
m.dsIndexProvider = dsIndexProvider
|
||||
m.leIndexProvider = leIndexProvider
|
||||
m.migrations = schemaversion.GetMigrations(dsIndexProvider, leIndexProvider)
|
||||
// Wrap the provider once with 10s caching for all conversions.
|
||||
// This prevents repeated DB queries across multiple conversion calls while allowing
|
||||
// the cache to refresh periodically, making it suitable for long-lived singleton usage.
|
||||
m.dsIndexProvider = schemaversion.WrapIndexProviderWithCache(dsIndexProvider)
|
||||
// Wrap library element provider with caching as well
|
||||
m.leIndexProvider = schemaversion.WrapLibraryElementProviderWithCache(leIndexProvider)
|
||||
m.migrations = schemaversion.GetMigrations(m.dsIndexProvider, m.leIndexProvider)
|
||||
close(m.ready)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ manifest: {
|
||||
|
||||
v0alpha1: {
|
||||
kinds: [examplev0alpha1]
|
||||
// This is explicitly set to false to keep the example app disabled by default.
|
||||
// This is explicitly set to false to keep the example app disabled by default.
|
||||
// It can be enabled via conf overrides, or by setting this value to true and regenerating.
|
||||
served: false
|
||||
}
|
||||
@@ -48,14 +48,14 @@ v1alpha1: {
|
||||
// served indicates whether this particular version is served by the API server.
|
||||
// served should be set to false before a version is removed from the manifest entirely.
|
||||
// served defaults to true if not present.
|
||||
// This is explicitly set to false to keep the example app disabled by default.
|
||||
// This is explicitly set to false to keep the example app disabled by default.
|
||||
// It can be enabled via conf overrides, or by setting this value to true and regenerating.
|
||||
served: false
|
||||
// routes contains resource routes for the version, which are split into 'namespaced' and 'cluster' scoped routes.
|
||||
// This allows you to add additional non-storage- and non-kind- based handlers for your app.
|
||||
// These should only be used if the behavior cannot be accomplished by reconciliation on storage events or subresource routes on a kind.
|
||||
routes: {
|
||||
// namespaced contains namespace-scoped resource routes for the version,
|
||||
// namespaced contains namespace-scoped resource routes for the version,
|
||||
// which are exposed as HTTP handlers on '<version>/namespaces/<namespace>/<route>'.
|
||||
namespaced: {
|
||||
"/something": {
|
||||
@@ -72,7 +72,7 @@ v1alpha1: {
|
||||
}
|
||||
}
|
||||
}
|
||||
// cluster contains cluster-scoped resource routes for the version,
|
||||
// cluster contains cluster-scoped resource routes for the version,
|
||||
// which are exposed as HTTP handlers on '<version>/<route>'.
|
||||
cluster: {
|
||||
"/other": {
|
||||
@@ -113,4 +113,4 @@ v1alpha1: {
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
include ../sdk.mk
|
||||
|
||||
.PHONY: generate # Run Grafana App SDK code generation
|
||||
generate: install-app-sdk update-app-sdk
|
||||
@$(APP_SDK_BIN) generate \
|
||||
--source=./kinds/ \
|
||||
--gogenpath=./pkg/apis \
|
||||
--grouping=group \
|
||||
--defencoding=none
|
||||
@@ -0,0 +1,92 @@
|
||||
module github.com/grafana/grafana/apps/quotas
|
||||
|
||||
go 1.25.3
|
||||
|
||||
require (
|
||||
github.com/grafana/grafana-app-sdk v0.48.5
|
||||
github.com/grafana/grafana-app-sdk/logging v0.48.3
|
||||
k8s.io/apimachinery v0.34.2
|
||||
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
|
||||
github.com/evanphx/json-patch v5.9.11+incompatible // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/getkin/kin-openapi v0.133.0 // 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.22.1 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.2 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-openapi/swag/jsonname v0.25.1 // indirect
|
||||
github.com/go-test/deep v1.1.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/google/gnostic-models v0.7.0 // indirect
|
||||
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/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
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/mailru/easyjson v0.9.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect
|
||||
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect
|
||||
github.com/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/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_golang v1.23.2 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.67.3 // indirect
|
||||
github.com/prometheus/procfs v0.16.1 // indirect
|
||||
github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/woodsbury/decimal128 v1.3.0 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/otel v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/oauth2 v0.33.0 // indirect
|
||||
golang.org/x/sync v0.18.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/term v0.37.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
golang.org/x/time v0.14.0 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 // indirect
|
||||
google.golang.org/grpc v1.77.0 // indirect
|
||||
google.golang.org/protobuf v1.36.10 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/api v0.34.2 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.34.2 // indirect
|
||||
k8s.io/client-go v0.34.2 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.1 // indirect
|
||||
sigs.k8s.io/yaml v1.6.0 // indirect
|
||||
)
|
||||
@@ -0,0 +1,252 @@
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous=
|
||||
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes=
|
||||
github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8=
|
||||
github.com/evanphx/json-patch v5.9.11+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/getkin/kin-openapi v0.133.0 h1:pJdmNohVIJ97r4AUFtEXRXwESr8b0bD721u/Tz6k8PQ=
|
||||
github.com/getkin/kin-openapi v0.133.0/go.mod h1:boAciF6cXk5FhPqe/NQeBTeenbjqU4LhWBf09ILVvWE=
|
||||
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=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-openapi/jsonpointer v0.22.1 h1:sHYI1He3b9NqJ4wXLoJDKmUmHkWy/L7rtEo92JUxBNk=
|
||||
github.com/go-openapi/jsonpointer v0.22.1/go.mod h1:pQT9OsLkfz1yWoMgYFy4x3U5GY5nUlsOn1qSBH5MkCM=
|
||||
github.com/go-openapi/jsonreference v0.21.2 h1:Wxjda4M/BBQllegefXrY/9aq1fxBA8sI5M/lFU6tSWU=
|
||||
github.com/go-openapi/jsonreference v0.21.2/go.mod h1:pp3PEjIsJ9CZDGCNOyXIQxsNuroxm8FAJ/+quA0yKzQ=
|
||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-openapi/swag/jsonname v0.25.1 h1:Sgx+qbwa4ej6AomWC6pEfXrA6uP2RkaNjA9BR8a1RJU=
|
||||
github.com/go-openapi/swag/jsonname v0.25.1/go.mod h1:71Tekow6UOLBD3wS7XhdT98g5J5GR13NOTQ9/6Q11Zo=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
|
||||
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
|
||||
github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
|
||||
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/grafana-app-sdk v0.48.2 h1:CQQDhwo1fWaXQVKvxxOcK6azbuY3E2TgJHNAZlYYn7U=
|
||||
github.com/grafana/grafana-app-sdk v0.48.2/go.mod h1:LDOvQ7OOyHLcXdSa0InATCa5OMoYAd6E1+rGLrMgHuk=
|
||||
github.com/grafana/grafana-app-sdk v0.48.4 h1:t9r+Y6E7D832ZxQ2c1n0lp6cvsYKhhrAodVYzE1y0s0=
|
||||
github.com/grafana/grafana-app-sdk v0.48.4/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y=
|
||||
github.com/grafana/grafana-app-sdk v0.48.5 h1:MS8l9fTZz+VbTfgApn09jw27GxhQ6fNOWGhC4ydvZmM=
|
||||
github.com/grafana/grafana-app-sdk v0.48.5/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.48.1 h1:veM0X5LAPyN3KsDLglWjIofndbGuf7MqnrDuDN+F/Ng=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.48.1/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.48.3/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
|
||||
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=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
|
||||
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY=
|
||||
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw=
|
||||
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c=
|
||||
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o=
|
||||
github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
|
||||
github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
|
||||
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
|
||||
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
|
||||
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
|
||||
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||
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.67.3 h1:shd26MlnwTw5jksTDhC7rTQIteBxy+ZZDr3t7F2xN2Q=
|
||||
github.com/prometheus/common v0.67.3/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI=
|
||||
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.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/spf13/pflag v1.0.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=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/woodsbury/decimal128 v1.3.0 h1:8pffMNWIlC0O5vbyHWFZAt5yWvWcrHA+3ovIIjVWss0=
|
||||
github.com/woodsbury/decimal128 v1.3.0/go.mod h1:C5UTmyTjW3JftjUFzOVhC20BEQa2a4ZKOB5I6Zjb+ds=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4=
|
||||
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
||||
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
|
||||
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo=
|
||||
golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
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.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.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.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
||||
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.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
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.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
|
||||
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0=
|
||||
gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
|
||||
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-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/api v0.0.0-20251022142026-3a174f9686a8 h1:mepRgnBZa07I4TRuomDE4sTIYieg/osKmzIf4USdWS4=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251002232023-7c0ddcbb5797 h1:CirRxTOwnRWVLKzDNrs0CXAaVozJoR4G9xvdRecrdpk=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251002232023-7c0ddcbb5797/go.mod h1:HSkG/KdJWusxU1F6CNrwNDjBMgisKxGnc5dAZfT0mjQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 h1:M1rk8KBnUsBDg1oPGHNCxG4vc1f49epmTO7xscSajMk=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||
google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A=
|
||||
google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
|
||||
google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
k8s.io/api v0.34.2 h1:fsSUNZhV+bnL6Aqrp6O7lMTy6o5x2C4XLjnh//8SLYY=
|
||||
k8s.io/api v0.34.2/go.mod h1:MMBPaWlED2a8w4RSeanD76f7opUoypY8TFYkSM+3XHw=
|
||||
k8s.io/apiextensions-apiserver v0.34.2 h1:WStKftnGeoKP4AZRz/BaAAEJvYp4mlZGN0UCv+uvsqo=
|
||||
k8s.io/apiextensions-apiserver v0.34.2/go.mod h1:398CJrsgXF1wytdaanynDpJ67zG4Xq7yj91GrmYN2SE=
|
||||
k8s.io/apimachinery v0.34.2 h1:zQ12Uk3eMHPxrsbUJgNF8bTauTVR2WgqJsTmwTE/NW4=
|
||||
k8s.io/apimachinery v0.34.2/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
|
||||
k8s.io/client-go v0.34.2 h1:Co6XiknN+uUZqiddlfAjT68184/37PS4QAzYvQvDR8M=
|
||||
k8s.io/client-go v0.34.2/go.mod h1:2VYDl1XXJsdcAxw7BenFslRQX28Dxz91U9MWKjX97fE=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE=
|
||||
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ=
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
|
||||
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
|
||||
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.1/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
|
||||
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
|
||||
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
|
||||
@@ -0,0 +1,2 @@
|
||||
module: "github.com/grafana/grafana/apps/quotas/kinds"
|
||||
language: version: "v0.8.2"
|
||||
@@ -0,0 +1,92 @@
|
||||
package kinds
|
||||
|
||||
manifest: {
|
||||
// appName is the unique name of your app. It is used to reference the app from other config objects,
|
||||
// and to generate the group used by your app in the app platform API.
|
||||
appName: "quotas"
|
||||
// groupOverride can be used to specify a non-appName-based API group.
|
||||
// By default, an app's API group is LOWER(REPLACE(appName, '-', '')).ext.grafana.com,
|
||||
// but there are cases where this needs to be changed.
|
||||
// Keep in mind that changing this after an app is deployed can cause problems with clients and/or kind data.
|
||||
groupOverride: "quotas.grafana.app"
|
||||
|
||||
// versions is a map of versions supported by your app. Version names should follow the format "v<integer>" or
|
||||
// "v<integer>(alpha|beta)<integer>". Each version contains the kinds your app manages for that version.
|
||||
// If your app needs access to kinds managed by another app, use permissions.accessKinds to allow your app access.
|
||||
versions: {
|
||||
"v0alpha1": v0alpha1
|
||||
}
|
||||
// extraPermissions contains any additional permissions your app may require to function.
|
||||
// Your app will always have all permissions for each kind it manages (the items defined in 'kinds').
|
||||
extraPermissions: {
|
||||
// If your app needs access to additional kinds supplied by other apps, you can list them here
|
||||
accessKinds: [
|
||||
// Here is an example for your app accessing the playlist kind for reads and watch
|
||||
// {
|
||||
// group: "playlist.grafana.app"
|
||||
// resource: "playlists"
|
||||
// actions: ["get","list","watch"]
|
||||
// }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// v1alpha1 is the v1alpha1 version of the app's API.
|
||||
// It includes kinds which the v1alpha1 API serves, and (future) custom routes served globally from the v1alpha1 version.
|
||||
v0alpha1: {
|
||||
// kinds is the list of kinds served by this version
|
||||
kinds: []
|
||||
// [OPTIONAL]
|
||||
// served indicates whether this particular version is served by the API server.
|
||||
// served should be set to false before a version is removed from the manifest entirely.
|
||||
// served defaults to true if not present.
|
||||
served: true
|
||||
|
||||
routes: {
|
||||
// namespaced contains namespace-scoped resource routes for the version,
|
||||
// which are exposed as HTTP handlers on '<version>/namespaces/<namespace>/<route>'.
|
||||
namespaced: {
|
||||
"/usage": {
|
||||
"GET": {
|
||||
response: {
|
||||
namespace: string
|
||||
resource: string
|
||||
group: string
|
||||
usage: int64
|
||||
limit: int64
|
||||
}
|
||||
request: {
|
||||
query: {
|
||||
group: string
|
||||
resource: string
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// [OPTIONAL]
|
||||
// Codegen is a trait that tells the grafana-app-sdk, or other code generation tooling, how to process this kind.
|
||||
// If not present, default values within the codegen trait are used.
|
||||
// If you wish to specify codegen per-version, put this section in the version's object
|
||||
// (for example, <no value>v1alpha1) instead.
|
||||
codegen: {
|
||||
// [OPTIONAL]
|
||||
// ts contains TypeScript code generation properties for the kind
|
||||
ts: {
|
||||
// [OPTIONAL]
|
||||
// enabled indicates whether the CLI should generate front-end TypeScript code for the kind.
|
||||
// Defaults to true if not present.
|
||||
enabled: true
|
||||
}
|
||||
// [OPTIONAL]
|
||||
// go contains go code generation properties for the kind
|
||||
go: {
|
||||
// [OPTIONAL]
|
||||
// enabled indicates whether the CLI should generate back-end go code for the kind.
|
||||
// Defaults to true if not present.
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
type GetUsageRequestParamsObject struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
GetUsageRequestParams `json:",inline"`
|
||||
}
|
||||
|
||||
func NewGetUsageRequestParamsObject() *GetUsageRequestParamsObject {
|
||||
return &GetUsageRequestParamsObject{}
|
||||
}
|
||||
|
||||
func (o *GetUsageRequestParamsObject) DeepCopyObject() runtime.Object {
|
||||
dst := NewGetUsageRequestParamsObject()
|
||||
o.DeepCopyInto(dst)
|
||||
return dst
|
||||
}
|
||||
|
||||
func (o *GetUsageRequestParamsObject) DeepCopyInto(dst *GetUsageRequestParamsObject) {
|
||||
dst.TypeMeta.APIVersion = o.TypeMeta.APIVersion
|
||||
dst.TypeMeta.Kind = o.TypeMeta.Kind
|
||||
dstGetUsageRequestParams := GetUsageRequestParams{}
|
||||
_ = resource.CopyObjectInto(&dstGetUsageRequestParams, &o.GetUsageRequestParams)
|
||||
}
|
||||
|
||||
var _ runtime.Object = NewGetUsageRequestParamsObject()
|
||||
@@ -0,0 +1,13 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
|
||||
package v0alpha1
|
||||
|
||||
type GetUsageRequestParams struct {
|
||||
Group string `json:"group"`
|
||||
Resource string `json:"resource"`
|
||||
}
|
||||
|
||||
// NewGetUsageRequestParams creates a new GetUsageRequestParams object.
|
||||
func NewGetUsageRequestParams() *GetUsageRequestParams {
|
||||
return &GetUsageRequestParams{}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
|
||||
package v0alpha1
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type GetUsageBody struct {
|
||||
Namespace string `json:"namespace"`
|
||||
Resource string `json:"resource"`
|
||||
Group string `json:"group"`
|
||||
Usage int64 `json:"usage"`
|
||||
Limit int64 `json:"limit"`
|
||||
}
|
||||
|
||||
// NewGetUsageBody creates a new GetUsageBody object.
|
||||
func NewGetUsageBody() *GetUsageBody {
|
||||
return &GetUsageBody{}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type GetUsage struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
GetUsageBody `json:",inline"`
|
||||
}
|
||||
|
||||
func NewGetUsage() *GetUsage {
|
||||
return &GetUsage{}
|
||||
}
|
||||
|
||||
func (t *GetUsageBody) DeepCopyInto(dst *GetUsageBody) {
|
||||
_ = resource.CopyObjectInto(dst, t)
|
||||
}
|
||||
|
||||
func (o *GetUsage) DeepCopyObject() runtime.Object {
|
||||
dst := NewGetUsage()
|
||||
o.DeepCopyInto(dst)
|
||||
return dst
|
||||
}
|
||||
|
||||
func (o *GetUsage) DeepCopyInto(dst *GetUsage) {
|
||||
dst.TypeMeta.APIVersion = o.TypeMeta.APIVersion
|
||||
dst.TypeMeta.Kind = o.TypeMeta.Kind
|
||||
o.GetUsageBody.DeepCopyInto(&dst.GetUsageBody)
|
||||
}
|
||||
|
||||
var _ runtime.Object = NewGetUsage()
|
||||
Generated
+213
@@ -0,0 +1,213 @@
|
||||
//
|
||||
// This file is generated by grafana-app-sdk
|
||||
// DO NOT EDIT
|
||||
//
|
||||
|
||||
package apis
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/app"
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/kube-openapi/pkg/spec3"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
|
||||
v0alpha1 "github.com/grafana/grafana/apps/quotas/pkg/apis/quotas/v0alpha1"
|
||||
)
|
||||
|
||||
var appManifestData = app.ManifestData{
|
||||
AppName: "quotas",
|
||||
Group: "quotas.grafana.app",
|
||||
PreferredVersion: "v0alpha1",
|
||||
Versions: []app.ManifestVersion{
|
||||
{
|
||||
Name: "v0alpha1",
|
||||
Served: true,
|
||||
Kinds: []app.ManifestVersionKind{},
|
||||
Routes: app.ManifestVersionRoutes{
|
||||
Namespaced: map[string]spec3.PathProps{
|
||||
"/usage": {
|
||||
Get: &spec3.Operation{
|
||||
OperationProps: spec3.OperationProps{
|
||||
|
||||
OperationId: "getUsage",
|
||||
|
||||
Parameters: []*spec3.Parameter{
|
||||
|
||||
{
|
||||
ParameterProps: spec3.ParameterProps{
|
||||
Name: "group",
|
||||
In: "query",
|
||||
Required: true,
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
ParameterProps: spec3.ParameterProps{
|
||||
Name: "resource",
|
||||
In: "query",
|
||||
Required: true,
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Responses: &spec3.Responses{
|
||||
ResponsesProps: spec3.ResponsesProps{
|
||||
Default: &spec3.Response{
|
||||
ResponseProps: spec3.ResponseProps{
|
||||
Description: "Default OK response",
|
||||
Content: map[string]*spec3.MediaType{
|
||||
"application/json": {
|
||||
MediaTypeProps: spec3.MediaTypeProps{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"apiVersion": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
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",
|
||||
},
|
||||
},
|
||||
"group": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
},
|
||||
},
|
||||
"kind": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
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",
|
||||
},
|
||||
},
|
||||
"limit": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"integer"},
|
||||
},
|
||||
},
|
||||
"namespace": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
},
|
||||
},
|
||||
"resource": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
},
|
||||
},
|
||||
"usage": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"integer"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{
|
||||
"namespace",
|
||||
"resource",
|
||||
"group",
|
||||
"usage",
|
||||
"limit",
|
||||
"apiVersion",
|
||||
"kind",
|
||||
},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Cluster: map[string]spec3.PathProps{},
|
||||
Schemas: map[string]spec.Schema{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func LocalManifest() app.Manifest {
|
||||
return app.NewEmbeddedManifest(appManifestData)
|
||||
}
|
||||
|
||||
func RemoteManifest() app.Manifest {
|
||||
return app.NewAPIServerManifest("quotas")
|
||||
}
|
||||
|
||||
var kindVersionToGoType = map[string]resource.Kind{}
|
||||
|
||||
// ManifestGoTypeAssociator returns the associated resource.Kind instance for a given Kind and Version, if one exists.
|
||||
// If there is no association for the provided Kind and Version, exists will return false.
|
||||
func ManifestGoTypeAssociator(kind, version string) (goType resource.Kind, exists bool) {
|
||||
goType, exists = kindVersionToGoType[fmt.Sprintf("%s/%s", kind, version)]
|
||||
return goType, exists
|
||||
}
|
||||
|
||||
var customRouteToGoResponseType = map[string]any{
|
||||
"v0alpha1||<namespace>/usage|GET": v0alpha1.GetUsage{},
|
||||
}
|
||||
|
||||
// ManifestCustomRouteResponsesAssociator returns the associated response go type for a given kind, version, custom route path, and method, if one exists.
|
||||
// kind may be empty for custom routes which are not kind subroutes. Leading slashes are removed from subroute paths.
|
||||
// If there is no association for the provided kind, version, custom route path, and method, exists will return false.
|
||||
// Resource routes (those without a kind) should prefix their route with "<namespace>/" if the route is namespaced (otherwise the route is assumed to be cluster-scope)
|
||||
func ManifestCustomRouteResponsesAssociator(kind, version, path, verb string) (goType any, exists bool) {
|
||||
if len(path) > 0 && path[0] == '/' {
|
||||
path = path[1:]
|
||||
}
|
||||
goType, exists = customRouteToGoResponseType[fmt.Sprintf("%s|%s|%s|%s", version, kind, path, strings.ToUpper(verb))]
|
||||
return goType, exists
|
||||
}
|
||||
|
||||
var customRouteToGoParamsType = map[string]runtime.Object{}
|
||||
|
||||
func ManifestCustomRouteQueryAssociator(kind, version, path, verb string) (goType runtime.Object, exists bool) {
|
||||
if len(path) > 0 && path[0] == '/' {
|
||||
path = path[1:]
|
||||
}
|
||||
goType, exists = customRouteToGoParamsType[fmt.Sprintf("%s|%s|%s|%s", version, kind, path, strings.ToUpper(verb))]
|
||||
return goType, exists
|
||||
}
|
||||
|
||||
var customRouteToGoRequestBodyType = map[string]any{}
|
||||
|
||||
func ManifestCustomRouteRequestBodyAssociator(kind, version, path, verb string) (goType any, exists bool) {
|
||||
if len(path) > 0 && path[0] == '/' {
|
||||
path = path[1:]
|
||||
}
|
||||
goType, exists = customRouteToGoRequestBodyType[fmt.Sprintf("%s|%s|%s|%s", version, kind, path, strings.ToUpper(verb))]
|
||||
return goType, exists
|
||||
}
|
||||
|
||||
type GoTypeAssociator struct{}
|
||||
|
||||
func NewGoTypeAssociator() *GoTypeAssociator {
|
||||
return &GoTypeAssociator{}
|
||||
}
|
||||
|
||||
func (g *GoTypeAssociator) KindToGoType(kind, version string) (goType resource.Kind, exists bool) {
|
||||
return ManifestGoTypeAssociator(kind, version)
|
||||
}
|
||||
func (g *GoTypeAssociator) CustomRouteReturnGoType(kind, version, path, verb string) (goType any, exists bool) {
|
||||
return ManifestCustomRouteResponsesAssociator(kind, version, path, verb)
|
||||
}
|
||||
func (g *GoTypeAssociator) CustomRouteQueryGoType(kind, version, path, verb string) (goType runtime.Object, exists bool) {
|
||||
return ManifestCustomRouteQueryAssociator(kind, version, path, verb)
|
||||
}
|
||||
func (g *GoTypeAssociator) CustomRouteRequestBodyGoType(kind, version, path, verb string) (goType any, exists bool) {
|
||||
return ManifestCustomRouteRequestBodyAssociator(kind, version, path, verb)
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/app"
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
"github.com/grafana/grafana-app-sdk/operator"
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
|
||||
unifiedStorage "github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/simple"
|
||||
quotasv0alpha1 "github.com/grafana/grafana/apps/quotas/pkg/apis/quotas/v0alpha1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
type QuotasAppConfig struct {
|
||||
ResourceClient unifiedStorage.ResourceClient
|
||||
}
|
||||
|
||||
type QuotasHandler struct {
|
||||
ResourceClient unifiedStorage.ResourceClient
|
||||
}
|
||||
|
||||
func NewQuotasHandler(cfg *QuotasAppConfig) *QuotasHandler {
|
||||
return &QuotasHandler{
|
||||
ResourceClient: cfg.ResourceClient,
|
||||
}
|
||||
}
|
||||
|
||||
// GetQuota handles requests for the GET /usage resource route
|
||||
func (h *QuotasHandler) GetQuota(ctx context.Context, writer app.CustomRouteResponseWriter, request *app.CustomRouteRequest) error {
|
||||
if !request.URL.Query().Has("group") {
|
||||
// TODO its returning a 500 instead of 400 bad request
|
||||
writer.WriteHeader(http.StatusBadRequest)
|
||||
return fmt.Errorf("missing required query parameters: group")
|
||||
}
|
||||
if !request.URL.Query().Has("resource") {
|
||||
writer.WriteHeader(http.StatusBadRequest)
|
||||
return fmt.Errorf("missing required query parameters: resource")
|
||||
}
|
||||
group := request.URL.Query().Get("group")
|
||||
res := request.URL.Query().Get("resource")
|
||||
|
||||
quotaReq := &resourcepb.QuotaUsageRequest{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: request.ResourceIdentifier.Namespace,
|
||||
Group: group,
|
||||
Resource: res,
|
||||
},
|
||||
}
|
||||
quota, err := h.ResourceClient.GetQuotaUsage(ctx, quotaReq)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
writer.Header().Set("Content-Type", "application/json")
|
||||
return json.NewEncoder(writer).Encode(quotasv0alpha1.GetUsage{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "quotas.grafana.com/v0alpha1",
|
||||
Kind: "Quotas",
|
||||
},
|
||||
GetUsageBody: quotasv0alpha1.GetUsageBody{
|
||||
Namespace: request.ResourceIdentifier.Namespace,
|
||||
Group: group,
|
||||
Resource: res,
|
||||
Usage: quota.Usage,
|
||||
Limit: quota.Limit,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func New(cfg app.Config) (app.App, error) {
|
||||
appConfig, ok := cfg.SpecificConfig.(*QuotasAppConfig)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected QuotasAppConfig but got %T", cfg.SpecificConfig)
|
||||
}
|
||||
handler := NewQuotasHandler(appConfig)
|
||||
|
||||
simpleConfig := simple.AppConfig{
|
||||
Name: "quotas",
|
||||
KubeConfig: cfg.KubeConfig,
|
||||
InformerConfig: simple.AppInformerConfig{
|
||||
InformerOptions: operator.InformerOptions{
|
||||
ErrorHandler: func(ctx context.Context, err error) {
|
||||
logging.FromContext(ctx).Error("Informer processing error", "error", err)
|
||||
},
|
||||
},
|
||||
},
|
||||
ManagedKinds: []simple.AppManagedKind{},
|
||||
VersionedCustomRoutes: map[string]simple.AppVersionRouteHandlers{
|
||||
"v0alpha1": {
|
||||
{
|
||||
Namespaced: true,
|
||||
Path: "usage",
|
||||
Method: "GET",
|
||||
}: handler.GetQuota,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
a, err := simple.NewApp(simpleConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = a.ValidateManifest(cfg.ManifestData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func GetKinds() map[schema.GroupVersion][]resource.Kind {
|
||||
return map[schema.GroupVersion][]resource.Kind{}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/app"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetQuota(t *testing.T) {
|
||||
t.Run("will return error when resource param is missing", func(t *testing.T) {
|
||||
clientMock := resource.NewMockResourceClient(t)
|
||||
handler := NewQuotasHandler(&QuotasAppConfig{
|
||||
ResourceClient: clientMock,
|
||||
})
|
||||
url, err := url.Parse("http://localhost:3000/apis/quotas.grafana.app/v0alpha1/namespaces/stacks-1/usage?group=dashboard.grafana.app")
|
||||
require.NoError(t, err)
|
||||
req := &app.CustomRouteRequest{
|
||||
URL: url,
|
||||
Method: "GET",
|
||||
}
|
||||
recorder := &httptest.ResponseRecorder{}
|
||||
err = handler.GetQuota(context.Background(), recorder, req)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("will return error when group param is missing", func(t *testing.T) {
|
||||
clientMock := resource.NewMockResourceClient(t)
|
||||
handler := NewQuotasHandler(&QuotasAppConfig{
|
||||
ResourceClient: clientMock,
|
||||
})
|
||||
url, err := url.Parse("http://localhost:3000/apis/quotas.grafana.app/v0alpha1/namespaces/stacks-1/usage?resource=dashboards")
|
||||
require.NoError(t, err)
|
||||
req := &app.CustomRouteRequest{
|
||||
URL: url,
|
||||
Method: "GET",
|
||||
}
|
||||
recorder := &httptest.ResponseRecorder{}
|
||||
err = handler.GetQuota(context.Background(), recorder, req)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("will return quotas response when params are valid", func(t *testing.T) {
|
||||
clientMock := resource.NewMockResourceClient(t)
|
||||
clientMock.On("GetQuotaUsage", mock.Anything, mock.Anything, mock.Anything).Return(&resourcepb.QuotaUsageResponse{
|
||||
Error: nil,
|
||||
Usage: 1,
|
||||
Limit: 2,
|
||||
}, nil)
|
||||
handler := NewQuotasHandler(&QuotasAppConfig{
|
||||
ResourceClient: clientMock,
|
||||
})
|
||||
url, err := url.Parse("http://localhost:3000/apis/quotas.grafana.app/v0alpha1/namespaces/stacks-1/usage?group=dashboard.grafana.app&resource=dashboards")
|
||||
require.NoError(t, err)
|
||||
req := &app.CustomRouteRequest{
|
||||
URL: url,
|
||||
Method: "GET",
|
||||
}
|
||||
recorder := &httptest.ResponseRecorder{}
|
||||
err = handler.GetQuota(context.Background(), recorder, req)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 200, recorder.Code)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* This file was generated by grafana-app-sdk. DO NOT EDIT.
|
||||
*/
|
||||
import { Spec } from './types.spec.gen';
|
||||
import { Status } from './types.status.gen';
|
||||
|
||||
export interface Metadata {
|
||||
name: string;
|
||||
namespace: string;
|
||||
generateName?: string;
|
||||
selfLink?: string;
|
||||
uid?: string;
|
||||
resourceVersion?: string;
|
||||
generation?: number;
|
||||
creationTimestamp?: string;
|
||||
deletionTimestamp?: string;
|
||||
deletionGracePeriodSeconds?: number;
|
||||
labels?: Record<string, string>;
|
||||
annotations?: Record<string, string>;
|
||||
ownerReferences?: OwnerReference[];
|
||||
finalizers?: string[];
|
||||
managedFields?: ManagedFieldsEntry[];
|
||||
}
|
||||
|
||||
export interface OwnerReference {
|
||||
apiVersion: string;
|
||||
kind: string;
|
||||
name: string;
|
||||
uid: string;
|
||||
controller?: boolean;
|
||||
blockOwnerDeletion?: boolean;
|
||||
}
|
||||
|
||||
export interface ManagedFieldsEntry {
|
||||
manager?: string;
|
||||
operation?: string;
|
||||
apiVersion?: string;
|
||||
time?: string;
|
||||
fieldsType?: string;
|
||||
subresource?: string;
|
||||
}
|
||||
|
||||
export interface Quota {
|
||||
kind: string;
|
||||
apiVersion: string;
|
||||
metadata: Metadata;
|
||||
spec: Spec;
|
||||
status: Status;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
|
||||
// metadata contains embedded CommonMetadata and can be extended with custom string fields
|
||||
// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here
|
||||
// without external reference as using the CommonMetadata reference breaks thema codegen.
|
||||
export interface Metadata {
|
||||
updateTimestamp: string;
|
||||
createdBy: string;
|
||||
uid: string;
|
||||
creationTimestamp: string;
|
||||
deletionTimestamp?: string;
|
||||
finalizers: string[];
|
||||
resourceVersion: string;
|
||||
generation: number;
|
||||
updatedBy: string;
|
||||
labels: Record<string, string>;
|
||||
}
|
||||
|
||||
export const defaultMetadata = (): Metadata => ({
|
||||
updateTimestamp: "",
|
||||
createdBy: "",
|
||||
uid: "",
|
||||
creationTimestamp: "",
|
||||
finalizers: [],
|
||||
resourceVersion: "",
|
||||
generation: 0,
|
||||
updatedBy: "",
|
||||
labels: {},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
|
||||
export interface Spec {
|
||||
count: string;
|
||||
limit: string;
|
||||
kind: string;
|
||||
}
|
||||
|
||||
export const defaultSpec = (): Spec => ({
|
||||
count: "",
|
||||
limit: "",
|
||||
kind: "",
|
||||
});
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
|
||||
export interface OperatorState {
|
||||
// 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";
|
||||
// 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?: Record<string, any>;
|
||||
}
|
||||
|
||||
export const defaultOperatorState = (): OperatorState => ({
|
||||
lastEvaluation: "",
|
||||
state: "success",
|
||||
});
|
||||
|
||||
export interface Status {
|
||||
// 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?: Record<string, OperatorState>;
|
||||
// additionalFields is reserved for future use
|
||||
additionalFields?: Record<string, any>;
|
||||
}
|
||||
|
||||
export const defaultStatus = (): Status => ({
|
||||
});
|
||||
|
||||
@@ -171,6 +171,7 @@ Query expressions are different for each data source. For more information, refe
|
||||
- If you need more room in a single input field query editor, then hover your cursor over the lines in the lower right corner of the field and drag downward to expand.
|
||||
|
||||
1. (Optional) In the **Regex** field, type a regular expression to filter or capture specific parts of the names returned by your data source query. To see examples, refer to [Filter variables with a regular expression](#filter-variables-with-regex).
|
||||
1. Under **Apply regex to**, select **Variable value** or **Display text** to choose where the regex pattern is applied. The default is **Variable value**.
|
||||
1. In the **Sort** drop-down list, select the sort order for values to be displayed in the dropdown list. The default option, **Disabled**, means that the order of options returned by your data source query is used.
|
||||
1. Under **Refresh**, select when the variable should update options:
|
||||
- **On dashboard load** - Queries the data source every time the dashboard loads. This slows down dashboard loading, because the variable query needs to be completed before dashboard can be initialized.
|
||||
|
||||
@@ -79,6 +79,16 @@ test.describe(
|
||||
await expect(regexInput).toHaveAttribute('placeholder', '/.*-(?<text>.*)-(?<value>.*)-.*/');
|
||||
await expect(regexInput).toHaveValue('');
|
||||
|
||||
// Check regex apply to field - should default to "Variable value"
|
||||
const regexApplyToField = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsRegExApplyToSelectV2
|
||||
);
|
||||
await expect(regexApplyToField).toBeVisible();
|
||||
const variableValueRadio = page.getByRole('radio', { name: 'Variable value' });
|
||||
await expect(variableValueRadio).toBeChecked();
|
||||
const displayTextRadio = page.getByRole('radio', { name: 'Display text' });
|
||||
await expect(displayTextRadio).not.toBeChecked();
|
||||
|
||||
const sortSelect = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsSortSelectV2
|
||||
);
|
||||
|
||||
@@ -2001,11 +2001,6 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"public/app/features/dashboard-scene/settings/variables/components/VariableTextAreaField.tsx": {
|
||||
"no-restricted-syntax": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"public/app/features/dashboard-scene/settings/variables/components/VariableTextField.tsx": {
|
||||
"no-restricted-syntax": {
|
||||
"count": 1
|
||||
|
||||
@@ -665,7 +665,7 @@ require (
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/platforms v0.2.1 // indirect
|
||||
github.com/cpuguy83/dockercfg v0.3.2 // indirect
|
||||
github.com/ebitengine/purego v0.8.4 // indirect
|
||||
github.com/ebitengine/purego v0.8.2 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-openapi/swag/conv v0.25.1 // indirect
|
||||
github.com/go-openapi/swag/fileutils v0.25.1 // indirect
|
||||
@@ -686,7 +686,7 @@ require (
|
||||
github.com/moby/term v0.5.0 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/shirou/gopsutil/v4 v4.25.6 // indirect
|
||||
github.com/shirou/gopsutil/v4 v4.25.3 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.14 // indirect
|
||||
github.com/tklauser/numcpus v0.8.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
|
||||
@@ -646,7 +646,6 @@ gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0p
|
||||
github.com/1NCE-GmbH/grpc-go-pool v0.0.0-20231117122434-2a5bb974daa2 h1:qFYgLH2zZe3WHpQgUrzeazC+ebDebwAQqS9yE1cP5Bs=
|
||||
github.com/1NCE-GmbH/grpc-go-pool v0.0.0-20231117122434-2a5bb974daa2/go.mod h1:09/ALd1AXCTCOfcJYD8+jIYKmFmi6PVCkTsipC18F7E=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U=
|
||||
github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=
|
||||
github.com/Azure/azure-sdk-for-go v23.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
@@ -1075,7 +1074,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/cznic/b v0.0.0-20180115125044-35e9bbe41f07/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8=
|
||||
github.com/cznic/fileutil v0.0.0-20180108211300-6a051e75936f/go.mod h1:8S58EK26zhXSxzv7NQFpnliaOQsmDUxvoQO3rt154Vg=
|
||||
github.com/cznic/golex v0.0.0-20170803123110-4ab7c5e190e4/go.mod h1:+bmmJDNmKlhWNG+gwWCkaBoTy39Fs+bzRxVBzoTQbIc=
|
||||
@@ -1142,8 +1140,8 @@ github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+m
|
||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
|
||||
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
|
||||
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/edsrzf/mmap-go v1.2.0 h1:hXLYlkbaPzt1SaQk+anYwKSRNhufIDCchSPkUD6dD84=
|
||||
@@ -2393,8 +2391,8 @@ github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah
|
||||
github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas=
|
||||
github.com/shadowspore/fossil-delta v0.0.0-20241213113458-1d797d70cbe3 h1:/4/IJi5iyTdh6mqOUaASW148HQpujYiHl0Wl78dSOSc=
|
||||
github.com/shadowspore/fossil-delta v0.0.0-20241213113458-1d797d70cbe3/go.mod h1:aJIMhRsunltJR926EB2MUg8qHemFQDreSB33pyto2Ps=
|
||||
github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs=
|
||||
github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c=
|
||||
github.com/shirou/gopsutil/v4 v4.25.3 h1:SeA68lsu8gLggyMbmCn8cmp97V1TI9ld9sVzAUcKcKE=
|
||||
github.com/shirou/gopsutil/v4 v4.25.3/go.mod h1:xbuxyoZj+UsgnZrENu3lQivsngRR5BdjbJwf2fv4szA=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||
|
||||
@@ -23,6 +23,7 @@ use (
|
||||
./apps/plugins
|
||||
./apps/preferences
|
||||
./apps/provisioning
|
||||
./apps/quotas
|
||||
./apps/scope
|
||||
./apps/secret
|
||||
./apps/shorturl
|
||||
|
||||
+6
-14
@@ -267,6 +267,8 @@ gioui.org v0.0.0-20210308172011-57750fc8a0a6 h1:K72hopUosKG3ntOPNG4OzzbuhxGuVf06
|
||||
git.sr.ht/~sbinet/gg v0.6.0 h1:RIzgkizAk+9r7uPzf/VfbJHBMKUr0F5hRFxTUGMnt38=
|
||||
git.sr.ht/~sbinet/gg v0.6.0/go.mod h1:uucygbfC9wVPQIfrmwM2et0imr8L7KQWywX0xpFMm94=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0/go.mod h1:OahwfttHWG6eJ0clwcfBAHoDI6X/LV/15hx/wlMZSrU=
|
||||
github.com/Azure/azure-amqp-common-go/v3 v3.2.3 h1:uDF62mbd9bypXWi19V1bN5NZEO84JqgmI5G73ibAmrk=
|
||||
github.com/Azure/azure-amqp-common-go/v3 v3.2.3/go.mod h1:7rPmbSfszeovxGfc5fSAXE4ehlXQZHpMja2OtxC2Tas=
|
||||
@@ -602,6 +604,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
|
||||
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/creasty/defaults v1.8.0 h1:z27FJxCAa0JKt3utc0sCImAEb+spPucmKoOdLHvHYKk=
|
||||
github.com/creasty/defaults v1.8.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM=
|
||||
github.com/crewjam/httperr v0.2.0 h1:b2BfXR8U3AlIHwNeFFvZ+BV1LFvKLlzMjzaTnZMybNo=
|
||||
@@ -680,8 +684,6 @@ github.com/eapache/go-resiliency v1.7.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 h1:Oy0F4ALJ04o5Qqpdz8XLIpNA3WM/iSIXqxtqo7UGVws=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3/go.mod h1:YvSRo5mw33fLEx1+DlK6L2VV43tJt5Eyel9n9XBcR+0=
|
||||
github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc=
|
||||
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
|
||||
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/efficientgo/tools/core v0.0.0-20220225185207-fe763185946b h1:ZHiD4/yE4idlbqvAO6iYCOYRzOMRpxkW+FKasRA3tsQ=
|
||||
github.com/efficientgo/tools/core v0.0.0-20220225185207-fe763185946b/go.mod h1:OmVcnJopJL8d3X3sSXTiypGoUSgFq1aDGmlrdi9dn/M=
|
||||
github.com/elastic/elastic-transport-go/v8 v8.6.1 h1:h2jQRqH6eLGiBSN4eZbQnJLtL4bC5b4lfVFRjw2R4e4=
|
||||
@@ -873,7 +875,6 @@ github.com/grafana/grafana-app-sdk v0.41.0 h1:SYHN3U7B1myRKY3UZZDkFsue9TDmAOap0U
|
||||
github.com/grafana/grafana-app-sdk v0.41.0/go.mod h1:Wg/3vEZfok1hhIWiHaaJm+FwkosfO98o8KbeLFEnZpY=
|
||||
github.com/grafana/grafana-app-sdk v0.46.0/go.mod h1:LCTrqR1SwBS13XGVYveBmM7giJDDjzuXK+M9VzPuPWc=
|
||||
github.com/grafana/grafana-app-sdk v0.47.0/go.mod h1:kywXmkppq0oReUMzkjTW8Fq2EBzyN7v914jttTWnWxA=
|
||||
github.com/grafana/grafana-app-sdk v0.48.2/go.mod h1:LDOvQ7OOyHLcXdSa0InATCa5OMoYAd6E1+rGLrMgHuk=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.38.0/go.mod h1:Y/bvbDhBiV/tkIle9RW49pgfSPIPSON8Q4qjx3pyqDk=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.39.0 h1:3GgN5+dUZYqq74Q+GT9/ET+yo+V54zWQk/Q2/JsJQB4=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.39.0/go.mod h1:WhDENSnaGHtyVVwZGVnAR7YLvh2xlLDYR3D7E6h7XVk=
|
||||
@@ -887,9 +888,10 @@ github.com/grafana/grafana-app-sdk/logging v0.45.0/go.mod h1:Gh/nBWnspK3oDNWtiM5
|
||||
github.com/grafana/grafana-app-sdk/logging v0.46.0/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.48.0 h1:xolkQxBlA2LQF4hprKIAeu+zUem1DigYZ6XC1TOhFJE=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.48.0/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.48.1/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.48.2 h1:tI+a9slUvxKUgweXDzUqkca2LWV3g1UdaSvwt8nQNHg=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.48.2/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.48.5 h1:vWiTZrsSscbC5IQq2heWXm0dXhvg40nXIeUTCdE9qsc=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.48.5/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
|
||||
github.com/grafana/grafana-app-sdk/plugin v0.41.0 h1:ShUvGpAVzM3UxcsfwS6l/lwW4ytDeTbCQXf8w2P8Yp8=
|
||||
github.com/grafana/grafana-app-sdk/plugin v0.41.0/go.mod h1:YIhimVfAqtOp3kdhxOanaSZjypVKh/bYxf9wfFfhDm0=
|
||||
github.com/grafana/grafana-aws-sdk v0.38.2 h1:TzQD0OpWsNjtldi5G5TLDlBRk8OyDf+B5ujcoAu4Dp0=
|
||||
@@ -956,7 +958,6 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9K
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
|
||||
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU=
|
||||
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw=
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
|
||||
@@ -1375,7 +1376,6 @@ github.com/richardartoul/molecule v1.0.0/go.mod h1:uvX/8buq8uVeiZiFht+0lqSLBHF+u
|
||||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww=
|
||||
github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY=
|
||||
@@ -1412,8 +1412,6 @@ github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKl
|
||||
github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4=
|
||||
github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
|
||||
github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI=
|
||||
github.com/shirou/gopsutil/v4 v4.25.3 h1:SeA68lsu8gLggyMbmCn8cmp97V1TI9ld9sVzAUcKcKE=
|
||||
github.com/shirou/gopsutil/v4 v4.25.3/go.mod h1:xbuxyoZj+UsgnZrENu3lQivsngRR5BdjbJwf2fv4szA=
|
||||
github.com/shirou/gopsutil/v4 v4.25.5/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c=
|
||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||
@@ -1593,7 +1591,6 @@ go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5queth
|
||||
go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
|
||||
go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/collector v0.121.0/go.mod h1:M4TlnmkjIgishm2DNCk9K3hMKTmAsY9w8cNFsp9EchM=
|
||||
go.opentelemetry.io/collector v0.124.0/go.mod h1:QzERYfmHUedawjr8Ph/CBEEkVqWS8IlxRLAZt+KHlCg=
|
||||
go.opentelemetry.io/collector/client v1.29.0/go.mod h1:LCUoEV2KCTKA1i+/txZaGsSPVWUcqeOV6wCfNsAippE=
|
||||
@@ -1880,7 +1877,6 @@ go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v8
|
||||
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
|
||||
go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc=
|
||||
go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
|
||||
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=
|
||||
@@ -2081,7 +2077,6 @@ google.golang.org/genproto/googleapis/api v0.0.0-20250728155136-f173205681a0/go.
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250804133106-a7a43d27e69b/go.mod h1:oDOGiMSXHL4sDTJvFvIB9nRQCGdLP1o/iVaqQK8zB+M=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c/go.mod h1:ea2MjsO70ssTfCjiwHgI0ZFqcw45Ksuk2ckf9G468GA=
|
||||
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/go.mod h1:U8EXRNSd8sUYyDfs/It7KVWodQr+Hf9xtxyxWudSwEw=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250929231259-57b25ae835d4/go.mod h1:NnuHhy+bxcg30o7FnVAZbXsPHUDQ9qKWAQKCD7VxFtk=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo=
|
||||
google.golang.org/genproto/googleapis/bytestream v0.0.0-20250603155806-513f23925822 h1:zWFRixYR5QlotL+Uv3YfsPRENIrQFXiGs+iwqel6fOQ=
|
||||
@@ -2112,7 +2107,6 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.
|
||||
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/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250929231259-57b25ae835d4/go.mod h1:HSkG/KdJWusxU1F6CNrwNDjBMgisKxGnc5dAZfT0mjQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251002232023-7c0ddcbb5797/go.mod h1:HSkG/KdJWusxU1F6CNrwNDjBMgisKxGnc5dAZfT0mjQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251014184007-4626949a642f/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||
@@ -2135,7 +2129,6 @@ google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7E
|
||||
google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM=
|
||||
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
|
||||
google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
|
||||
google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
|
||||
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=
|
||||
google.golang.org/grpc/examples v0.0.0-20230224211313-3775f633ce20/go.mod h1:Nr5H8+MlGWr5+xX/STzdoEqJrO+YteqFbMyCsrb6mH0=
|
||||
@@ -2250,7 +2243,6 @@ sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ih
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 h1:nbCitCK2hfnhyiKo6uf2HxUPTCodY6Qaf85SbDIaMBk=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.5.0/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.2.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
|
||||
|
||||
@@ -218,6 +218,8 @@ lineage: schemas: [{
|
||||
// Optional field, if you want to extract part of a series name or metric node segment.
|
||||
// Named capture groups can be used to separate the display text and value.
|
||||
regex?: string
|
||||
// Determine whether regex applies to variable value or display text
|
||||
regexApplyTo?: #VariableRegexApplyTo
|
||||
// Additional static options for query variable
|
||||
staticOptions?: [...#VariableOption]
|
||||
// Ordering of static options in relation to options returned from data source for query variable
|
||||
@@ -245,6 +247,10 @@ lineage: schemas: [{
|
||||
// Accepted values are 0 (show label and value), 1 (show value only), 2 (show nothing), 3 (show under the controls dropdown menu).
|
||||
#VariableHide: 0 | 1 | 2 | 3 @cuetsy(kind="enum",memberNames="dontHide|hideLabel|hideVariable|inControlsMenu") @grafana(TSVeneer="type")
|
||||
|
||||
// Determine whether regex applies to variable value or display text
|
||||
// Accepted values are "value" (apply to value used in queries) or "text" (apply to display text shown to users)
|
||||
#VariableRegexApplyTo: "value" | "text" @cuetsy(kind="type")
|
||||
|
||||
// Sort variable options
|
||||
// Accepted values are:
|
||||
// `0`: No sorting
|
||||
|
||||
+2
-2
@@ -296,8 +296,8 @@
|
||||
"@grafana/plugin-ui": "^0.11.1",
|
||||
"@grafana/prometheus": "workspace:*",
|
||||
"@grafana/runtime": "workspace:*",
|
||||
"@grafana/scenes": "6.47.1",
|
||||
"@grafana/scenes-react": "6.47.1",
|
||||
"@grafana/scenes": "6.49.0",
|
||||
"@grafana/scenes-react": "6.49.0",
|
||||
"@grafana/schema": "workspace:*",
|
||||
"@grafana/sql": "workspace:*",
|
||||
"@grafana/ui": "workspace:*",
|
||||
|
||||
@@ -521,6 +521,7 @@ export {
|
||||
VariableRefresh,
|
||||
VariableSort,
|
||||
VariableHide,
|
||||
type VariableRegexApplyTo,
|
||||
type VariableType,
|
||||
type VariableModel,
|
||||
type TypedVariableModel,
|
||||
|
||||
@@ -261,6 +261,10 @@ export interface FeatureToggles {
|
||||
*/
|
||||
kubernetesCorrelations?: boolean;
|
||||
/**
|
||||
* Adds support for Kubernetes unified storage quotas
|
||||
*/
|
||||
kubernetesUnifiedStorageQuotas?: boolean;
|
||||
/**
|
||||
* Adds support for Kubernetes logs drilldown
|
||||
*/
|
||||
kubernetesLogsDrilldown?: boolean;
|
||||
@@ -1220,4 +1224,8 @@ export interface FeatureToggles {
|
||||
* @default false
|
||||
*/
|
||||
useMTPlugins?: boolean;
|
||||
/**
|
||||
* Enables support for variables whose values can have multiple properties
|
||||
*/
|
||||
multiPropsVariables?: boolean;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ export enum VariableRefresh {
|
||||
onTimeRangeChanged,
|
||||
}
|
||||
|
||||
export type VariableRegexApplyTo = 'value' | 'text';
|
||||
|
||||
export enum VariableSort {
|
||||
disabled,
|
||||
alphabeticalAsc,
|
||||
@@ -117,6 +119,7 @@ export interface QueryVariableModel extends VariableWithMultiSupport {
|
||||
queryValue?: string;
|
||||
query: any;
|
||||
regex: string;
|
||||
regexApplyTo?: VariableRegexApplyTo;
|
||||
refresh: VariableRefresh;
|
||||
staticOptions?: VariableOption[];
|
||||
staticOptionsOrder?: 'before' | 'after' | 'sorted';
|
||||
|
||||
@@ -508,6 +508,9 @@ export const versionedPages = {
|
||||
queryOptionsRegExInputV2: {
|
||||
[MIN_GRAFANA_VERSION]: 'data-testid Variable editor Form Query RegEx field',
|
||||
},
|
||||
queryOptionsRegExApplyToSelectV2: {
|
||||
[MIN_GRAFANA_VERSION]: 'data-testid Variable editor Form Query RegExApplyTo select',
|
||||
},
|
||||
queryOptionsSortSelect: {
|
||||
[MIN_GRAFANA_VERSION]: 'Variable editor Form Query Sort select',
|
||||
},
|
||||
|
||||
Generated
+1
@@ -12,6 +12,7 @@ export type {
|
||||
AnnotationTarget,
|
||||
AnnotationPanelFilter,
|
||||
VariableOption,
|
||||
VariableRegexApplyTo,
|
||||
DashboardLink,
|
||||
DashboardLinkType,
|
||||
DashboardLinkPlacement,
|
||||
|
||||
@@ -187,6 +187,10 @@ export interface VariableModel {
|
||||
* Named capture groups can be used to separate the display text and value.
|
||||
*/
|
||||
regex?: string;
|
||||
/**
|
||||
* Determine whether regex applies to variable value or display text
|
||||
*/
|
||||
regexApplyTo?: VariableRegexApplyTo;
|
||||
/**
|
||||
* Whether the variable value should be managed by URL query params or not
|
||||
*/
|
||||
@@ -259,6 +263,12 @@ export enum VariableHide {
|
||||
inControlsMenu = 3,
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether regex applies to variable value or display text
|
||||
* Accepted values are "value" (apply to value used in queries) or "text" (apply to display text shown to users)
|
||||
*/
|
||||
export type VariableRegexApplyTo = ('value' | 'text');
|
||||
|
||||
/**
|
||||
* Sort variable options
|
||||
* Accepted values are:
|
||||
|
||||
@@ -293,6 +293,7 @@ export const handyTestingSchema: Spec = {
|
||||
},
|
||||
refresh: 'onDashboardLoad',
|
||||
regex: 'regex1',
|
||||
regexApplyTo: 'value',
|
||||
skipUrlSync: false,
|
||||
sort: 'disabled',
|
||||
allowCustomValue: true,
|
||||
|
||||
@@ -1105,6 +1105,7 @@ export interface QueryVariableSpec {
|
||||
datasource?: DataSourceRef;
|
||||
query: DataQueryKind;
|
||||
regex: string;
|
||||
regexApplyTo?: VariableRegexApplyTo;
|
||||
sort: VariableSort;
|
||||
definition?: string;
|
||||
options: VariableOption[];
|
||||
@@ -1125,6 +1126,7 @@ export const defaultQueryVariableSpec = (): QueryVariableSpec => ({
|
||||
skipUrlSync: false,
|
||||
query: defaultDataQueryKind(),
|
||||
regex: "",
|
||||
regexApplyTo: "value",
|
||||
sort: "disabled",
|
||||
options: [],
|
||||
multi: false,
|
||||
@@ -1161,6 +1163,12 @@ export type VariableRefresh = "never" | "onDashboardLoad" | "onTimeRangeChanged"
|
||||
|
||||
export const defaultVariableRefresh = (): VariableRefresh => ("never");
|
||||
|
||||
// Determine whether regex applies to variable value or display text
|
||||
// Accepted values are `value` (apply to value used in queries) or `text` (apply to display text shown to users)
|
||||
export type VariableRegexApplyTo = "value" | "text";
|
||||
|
||||
export const defaultVariableRegexApplyTo = (): VariableRegexApplyTo => ("value");
|
||||
|
||||
// Sort variable options
|
||||
// Accepted values are:
|
||||
// `disabled`: No sorting
|
||||
|
||||
@@ -1111,6 +1111,7 @@ export interface QueryVariableSpec {
|
||||
description?: string;
|
||||
query: DataQueryKind;
|
||||
regex: string;
|
||||
regexApplyTo?: VariableRegexApplyTo;
|
||||
sort: VariableSort;
|
||||
definition?: string;
|
||||
options: VariableOption[];
|
||||
@@ -1131,6 +1132,7 @@ export const defaultQueryVariableSpec = (): QueryVariableSpec => ({
|
||||
skipUrlSync: false,
|
||||
query: defaultDataQueryKind(),
|
||||
regex: "",
|
||||
regexApplyTo: "value",
|
||||
sort: "disabled",
|
||||
options: [],
|
||||
multi: false,
|
||||
@@ -1167,6 +1169,12 @@ export type VariableRefresh = "never" | "onDashboardLoad" | "onTimeRangeChanged"
|
||||
|
||||
export const defaultVariableRefresh = (): VariableRefresh => ("never");
|
||||
|
||||
// Determine whether regex applies to variable value or display text
|
||||
// Accepted values are `value` (apply to value used in queries) or `text` (apply to display text shown to users)
|
||||
export type VariableRegexApplyTo = "value" | "text";
|
||||
|
||||
export const defaultVariableRegexApplyTo = (): VariableRegexApplyTo => ("value");
|
||||
|
||||
// Sort variable options
|
||||
// Accepted values are:
|
||||
// `disabled`: No sorting
|
||||
|
||||
@@ -56,7 +56,8 @@ import (
|
||||
_ "github.com/grafana/e2e"
|
||||
_ "github.com/grafana/gofpdf"
|
||||
_ "github.com/grafana/gomemcache/memcache"
|
||||
_ "github.com/grafana/tempo/pkg/traceql"
|
||||
|
||||
_ "github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1"
|
||||
_ "github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1"
|
||||
_ "github.com/grafana/tempo/pkg/traceql"
|
||||
)
|
||||
|
||||
+11
@@ -834,6 +834,8 @@ type VariableModel struct {
|
||||
// Optional field, if you want to extract part of a series name or metric node segment.
|
||||
// Named capture groups can be used to separate the display text and value.
|
||||
Regex *string `json:"regex,omitempty"`
|
||||
// Determine whether regex applies to variable value or display text
|
||||
RegexApplyTo *VariableRegexApplyTo `json:"regexApplyTo,omitempty"`
|
||||
// Additional static options for query variable
|
||||
StaticOptions []VariableOption `json:"staticOptions,omitempty"`
|
||||
// Ordering of static options in relation to options returned from data source for query variable
|
||||
@@ -942,6 +944,15 @@ const (
|
||||
VariableSortNaturalDesc VariableSort = 8
|
||||
)
|
||||
|
||||
// Determine whether regex applies to variable value or display text
|
||||
// Accepted values are "value" (apply to value used in queries) or "text" (apply to display text shown to users)
|
||||
type VariableRegexApplyTo string
|
||||
|
||||
const (
|
||||
VariableRegexApplyToValue VariableRegexApplyTo = "value"
|
||||
VariableRegexApplyToText VariableRegexApplyTo = "text"
|
||||
)
|
||||
|
||||
// Contains the list of annotations that are associated with the dashboard.
|
||||
// Annotations are used to overlay event markers and overlay event tags on graphs.
|
||||
// Grafana comes with a native annotation store and the ability to add annotation events directly from the graph panel or via the HTTP API.
|
||||
|
||||
@@ -95,3 +95,7 @@ func (d *directResourceClient) BulkProcess(ctx context.Context, opts ...grpc.Cal
|
||||
func (b *directResourceClient) RebuildIndexes(ctx context.Context, req *resourcepb.RebuildIndexesRequest, opts ...grpc.CallOption) (*resourcepb.RebuildIndexesResponse, error) {
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (b *directResourceClient) GetQuotaUsage(ctx context.Context, req *resourcepb.QuotaUsageRequest, opts ...grpc.CallOption) (*resourcepb.QuotaUsageResponse, error) {
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
@@ -1103,3 +1103,7 @@ func (m *MockClient) BulkProcess(ctx context.Context, opts ...grpc.CallOption) (
|
||||
func (m *MockClient) UpdateIndex(ctx context.Context, reason string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockClient) GetQuotaUsage(ctx context.Context, req *resourcepb.QuotaUsageRequest, opts ...grpc.CallOption) (*resourcepb.QuotaUsageResponse, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -284,3 +284,6 @@ func (m *MockClient) BulkProcess(ctx context.Context, opts ...grpc.CallOption) (
|
||||
func (m *MockClient) UpdateIndex(ctx context.Context, reason string) error {
|
||||
return nil
|
||||
}
|
||||
func (m *MockClient) GetQuotaUsage(ctx context.Context, in *resourcepb.QuotaUsageRequest, opts ...grpc.CallOption) (*resourcepb.QuotaUsageResponse, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package appregistry
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana/pkg/registry/apps/quotas"
|
||||
"github.com/open-feature/go-sdk/openfeature"
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/app"
|
||||
@@ -44,12 +46,17 @@ func ProvideAppInstallers(
|
||||
exampleAppInstaller *example.ExampleAppInstaller,
|
||||
advisorAppInstaller *advisor.AdvisorAppInstaller,
|
||||
alertingHistorianAppInstaller *historian.AlertingHistorianAppInstaller,
|
||||
quotasAppInstaller *quotas.QuotasAppInstaller,
|
||||
) []appsdkapiserver.AppInstaller {
|
||||
featureClient := openfeature.NewDefaultClient()
|
||||
installers := []appsdkapiserver.AppInstaller{
|
||||
playlistAppInstaller,
|
||||
pluginsApplInstaller,
|
||||
exampleAppInstaller,
|
||||
}
|
||||
if featureClient.Boolean(context.Background(), featuremgmt.FlagKubernetesUnifiedStorageQuotas, false, openfeature.TransactionContext(context.Background())) {
|
||||
installers = append(installers, quotasAppInstaller)
|
||||
}
|
||||
//nolint:staticcheck // not yet migrated to OpenFeature
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagKubernetesShortURLs) {
|
||||
installers = append(installers, shorturlAppInstaller)
|
||||
|
||||
@@ -3,6 +3,7 @@ package appregistry
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/registry/apps/quotas"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/registry/apps/advisor"
|
||||
@@ -27,6 +28,7 @@ func TestProvideAppInstallers_Table(t *testing.T) {
|
||||
exampleAppInstaller := &example.ExampleAppInstaller{}
|
||||
advisorAppInstaller := &advisor.AdvisorAppInstaller{}
|
||||
historianAppInstaller := &historian.AlertingHistorianAppInstaller{}
|
||||
quotasAppInstaller := "as.QuotasAppInstaller{}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -43,7 +45,7 @@ func TestProvideAppInstallers_Table(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
features := featuremgmt.WithFeatures(tt.flags...)
|
||||
got := ProvideAppInstallers(features, playlistInstaller, pluginsInstaller, nil, tt.rulesInst, correlationsAppInstaller, notificationsAppInstaller, nil, annotationAppInstaller, exampleAppInstaller, advisorAppInstaller, historianAppInstaller)
|
||||
got := ProvideAppInstallers(features, playlistInstaller, pluginsInstaller, nil, tt.rulesInst, correlationsAppInstaller, notificationsAppInstaller, nil, annotationAppInstaller, exampleAppInstaller, advisorAppInstaller, historianAppInstaller, quotasAppInstaller)
|
||||
if tt.expectRulesApp {
|
||||
require.Contains(t, got, tt.rulesInst)
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package quotas
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/apps/quotas/pkg/apis"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/app"
|
||||
appsdkapiserver "github.com/grafana/grafana-app-sdk/k8s/apiserver"
|
||||
"github.com/grafana/grafana-app-sdk/simple"
|
||||
quotasapp "github.com/grafana/grafana/apps/quotas/pkg/app"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
var (
|
||||
_ appsdkapiserver.AppInstaller = (*QuotasAppInstaller)(nil)
|
||||
)
|
||||
|
||||
type QuotasAppInstaller struct {
|
||||
appsdkapiserver.AppInstaller
|
||||
cfg *setting.Cfg
|
||||
}
|
||||
|
||||
func RegisterAppInstaller(
|
||||
cfg *setting.Cfg,
|
||||
features featuremgmt.FeatureToggles,
|
||||
resourceClient resource.ResourceClient,
|
||||
) (*QuotasAppInstaller, error) {
|
||||
installer := &QuotasAppInstaller{
|
||||
cfg: cfg,
|
||||
}
|
||||
specificConfig := "asapp.QuotasAppConfig{
|
||||
ResourceClient: resourceClient,
|
||||
}
|
||||
provider := simple.NewAppProvider(apis.LocalManifest(), specificConfig, quotasapp.New)
|
||||
|
||||
appConfig := app.Config{
|
||||
KubeConfig: restclient.Config{}, // this will be overridden by the installer's InitializeApp method
|
||||
ManifestData: *apis.LocalManifest().ManifestData,
|
||||
SpecificConfig: specificConfig,
|
||||
}
|
||||
i, err := appsdkapiserver.NewDefaultAppInstaller(provider, appConfig, apis.NewGoTypeAssociator())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
installer.AppInstaller = i
|
||||
|
||||
return installer, nil
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package appregistry
|
||||
|
||||
import (
|
||||
"github.com/google/wire"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/quotas"
|
||||
|
||||
"github.com/grafana/grafana/pkg/registry/apps/alerting/historian"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/alerting/notifications"
|
||||
@@ -29,5 +30,6 @@ var WireSet = wire.NewSet(
|
||||
historian.RegisterAppInstaller,
|
||||
logsdrilldown.RegisterAppInstaller,
|
||||
annotation.RegisterAppInstaller,
|
||||
quotas.RegisterAppInstaller,
|
||||
example.RegisterAppInstaller,
|
||||
)
|
||||
|
||||
Generated
+11
-2
@@ -89,6 +89,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/registry/apps/logsdrilldown"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/playlist"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/plugins"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/quotas"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/shorturl"
|
||||
"github.com/grafana/grafana/pkg/registry/backgroundsvcs"
|
||||
"github.com/grafana/grafana/pkg/registry/usagestatssvcs"
|
||||
@@ -824,7 +825,11 @@ func Initialize(ctx context.Context, cfg *setting.Cfg, opts Options, apiOpts api
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v2 := appregistry.ProvideAppInstallers(featureToggles, playlistAppInstaller, appInstaller, shortURLAppInstaller, alertingRulesAppInstaller, correlationsAppInstaller, alertingNotificationsAppInstaller, logsDrilldownAppInstaller, annotationAppInstaller, exampleAppInstaller, advisorAppInstaller, alertingHistorianAppInstaller)
|
||||
quotasAppInstaller, err := quotas.RegisterAppInstaller(cfg, featureToggles, resourceClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v2 := appregistry.ProvideAppInstallers(featureToggles, playlistAppInstaller, appInstaller, shortURLAppInstaller, alertingRulesAppInstaller, correlationsAppInstaller, alertingNotificationsAppInstaller, logsDrilldownAppInstaller, annotationAppInstaller, exampleAppInstaller, advisorAppInstaller, alertingHistorianAppInstaller, quotasAppInstaller)
|
||||
builderMetrics := builder.ProvideBuilderMetrics(registerer)
|
||||
apiserverService, err := apiserver.ProvideService(cfg, featureToggles, routeRegisterImpl, tracingService, serverLockService, sqlStore, kvStore, middlewareHandler, scopedPluginDatasourceProvider, plugincontextProvider, pluginstoreService, dualwriteService, resourceClient, inlineSecureValueSupport, eventualRestConfigProvider, v, eventualRestConfigProvider, registerer, aggregatorRunner, v2, builderMetrics)
|
||||
if err != nil {
|
||||
@@ -1477,7 +1482,11 @@ func InitializeForTest(ctx context.Context, t sqlutil.ITestDB, testingT interfac
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v2 := appregistry.ProvideAppInstallers(featureToggles, playlistAppInstaller, appInstaller, shortURLAppInstaller, alertingRulesAppInstaller, correlationsAppInstaller, alertingNotificationsAppInstaller, logsDrilldownAppInstaller, annotationAppInstaller, exampleAppInstaller, advisorAppInstaller, alertingHistorianAppInstaller)
|
||||
quotasAppInstaller, err := quotas.RegisterAppInstaller(cfg, featureToggles, resourceClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v2 := appregistry.ProvideAppInstallers(featureToggles, playlistAppInstaller, appInstaller, shortURLAppInstaller, alertingRulesAppInstaller, correlationsAppInstaller, alertingNotificationsAppInstaller, logsDrilldownAppInstaller, annotationAppInstaller, exampleAppInstaller, advisorAppInstaller, alertingHistorianAppInstaller, quotasAppInstaller)
|
||||
builderMetrics := builder.ProvideBuilderMetrics(registerer)
|
||||
apiserverService, err := apiserver.ProvideService(cfg, featureToggles, routeRegisterImpl, tracingService, serverLockService, sqlStore, kvStore, middlewareHandler, scopedPluginDatasourceProvider, plugincontextProvider, pluginstoreService, dualwriteService, resourceClient, inlineSecureValueSupport, eventualRestConfigProvider, v, eventualRestConfigProvider, registerer, aggregatorRunner, v2, builderMetrics)
|
||||
if err != nil {
|
||||
|
||||
@@ -414,6 +414,13 @@ var (
|
||||
Owner: grafanaDataProSquad,
|
||||
RequiresRestart: true,
|
||||
},
|
||||
{
|
||||
Name: "kubernetesUnifiedStorageQuotas",
|
||||
Description: "Adds support for Kubernetes unified storage quotas",
|
||||
Stage: FeatureStageExperimental,
|
||||
Owner: grafanaSearchAndStorageSquad,
|
||||
RequiresRestart: true,
|
||||
},
|
||||
{
|
||||
Name: "kubernetesLogsDrilldown",
|
||||
Description: "Adds support for Kubernetes logs drilldown",
|
||||
@@ -2015,6 +2022,13 @@ var (
|
||||
FrontendOnly: true,
|
||||
Expression: "false",
|
||||
},
|
||||
{
|
||||
Name: "multiPropsVariables",
|
||||
Description: "Enables support for variables whose values can have multiple properties",
|
||||
Stage: FeatureStageExperimental,
|
||||
FrontendOnly: true,
|
||||
Owner: grafanaDashboardsSquad,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -244,7 +244,6 @@ addFieldFromCalculationStatFunctions,2023-11-03T14:39:58Z,2025-11-17T15:58:43Z,6
|
||||
pdfTables,2023-11-06T13:39:22Z,,95b48339f89c7d267bce7d894404d26ccd75d0e3,Agnès Toulet
|
||||
newVizTooltips,2023-11-06T16:35:59Z,2024-04-03T00:32:01Z,6b4b7127544865b78f712d907e6f1719595f4232,Adela Almasan
|
||||
ssoSettingsApi,2023-11-08T09:50:01Z,2025-07-03T08:53:33Z,5285e9503be5702680acb2b52a6bda0632f4603d,Misi
|
||||
logsInfiniteScrolling,2023-11-09T10:54:03Z,,174c2ab45a2af912519153c5c3e671f04396d7d7,Matias Chomicki
|
||||
flameGraphItemCollapsing,2023-11-09T14:31:07Z,2024-07-15T12:45:41Z,494a07b522df4e3ff9512b47767745a94c15f080,Andrej Ocenas
|
||||
alertingDetailsViewV2,2023-11-09T17:35:03Z,2024-03-14T14:18:01Z,323ee7c38ceb18b8e71c780d797fabac7041a673,Gilles De Mey
|
||||
alertingSimplifiedRouting,2023-11-10T13:14:39Z,2025-05-09T13:30:56Z,68e37c3925080cf64a5e7570eabb042f19ca2dbf,Sonia Aguilar
|
||||
|
||||
|
Generated
+2
@@ -56,6 +56,7 @@ kubernetesShortURLs,experimental,@grafana/grafana-app-platform-squad,false,true,
|
||||
useKubernetesShortURLsAPI,experimental,@grafana/sharing-squad,false,false,true
|
||||
kubernetesAlertingRules,experimental,@grafana/alerting-squad,false,true,false
|
||||
kubernetesCorrelations,experimental,@grafana/datapro,false,true,false
|
||||
kubernetesUnifiedStorageQuotas,experimental,@grafana/search-and-storage,false,true,false
|
||||
kubernetesLogsDrilldown,experimental,@grafana/observability-logs,false,true,false
|
||||
kubernetesQueryCaching,experimental,@grafana/grafana-operator-experience-squad,false,true,false
|
||||
dashboardDisableSchemaValidationV1,experimental,@grafana/grafana-app-platform-squad,false,false,false
|
||||
@@ -273,3 +274,4 @@ lokiQueryLimitsContext,experimental,@grafana/observability-logs,false,false,true
|
||||
rudderstackUpgrade,experimental,@grafana/grafana-frontend-platform,false,false,true
|
||||
kubernetesAlertingHistorian,experimental,@grafana/alerting-squad,false,true,false
|
||||
useMTPlugins,experimental,@grafana/plugins-platform-backend,false,false,true
|
||||
multiPropsVariables,experimental,@grafana/dashboards-squad,false,false,true
|
||||
|
||||
|
Generated
+4
@@ -183,6 +183,10 @@ const (
|
||||
// Adds support for Kubernetes correlations
|
||||
FlagKubernetesCorrelations = "kubernetesCorrelations"
|
||||
|
||||
// FlagKubernetesUnifiedStorageQuotas
|
||||
// Adds support for Kubernetes unified storage quotas
|
||||
FlagKubernetesUnifiedStorageQuotas = "kubernetesUnifiedStorageQuotas"
|
||||
|
||||
// FlagKubernetesLogsDrilldown
|
||||
// Adds support for Kubernetes logs drilldown
|
||||
FlagKubernetesLogsDrilldown = "kubernetesLogsDrilldown"
|
||||
|
||||
+26
@@ -2039,6 +2039,19 @@
|
||||
"requiresRestart": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "kubernetesUnifiedStorageQuotas",
|
||||
"resourceVersion": "1764965198011",
|
||||
"creationTimestamp": "2025-12-05T20:06:38Z"
|
||||
},
|
||||
"spec": {
|
||||
"description": "Adds support for Kubernetes unified storage quotas",
|
||||
"stage": "experimental",
|
||||
"codeowner": "@grafana/search-and-storage",
|
||||
"requiresRestart": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "localeFormatPreference",
|
||||
@@ -2259,6 +2272,19 @@
|
||||
"codeowner": "@grafana/alerting-squad"
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "multiPropsVariables",
|
||||
"resourceVersion": "1765293230587",
|
||||
"creationTimestamp": "2025-12-09T15:13:50Z"
|
||||
},
|
||||
"spec": {
|
||||
"description": "Enables support for variables whose values can have multiple properties",
|
||||
"stage": "experimental",
|
||||
"codeowner": "@grafana/dashboards-squad",
|
||||
"frontend": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "multiTenantTempCredentials",
|
||||
|
||||
@@ -166,6 +166,7 @@ type resourceClientMock struct {
|
||||
resourcepb.BulkStoreClient
|
||||
resourcepb.BlobStoreClient
|
||||
resourcepb.DiagnosticsClient
|
||||
resourcepb.QuotasClient
|
||||
}
|
||||
|
||||
// always return GRPC Unauthenticated code
|
||||
|
||||
@@ -587,6 +587,22 @@ message ResourceTableRow {
|
||||
bytes object = 4;
|
||||
}
|
||||
|
||||
message QuotaUsageRequest {
|
||||
// Namespace (tenant)
|
||||
ResourceKey key = 1;
|
||||
}
|
||||
|
||||
message QuotaUsageResponse {
|
||||
// Error details
|
||||
ErrorResult error = 1;
|
||||
|
||||
// Current usage
|
||||
int64 usage = 2;
|
||||
|
||||
// Current limit
|
||||
int64 limit = 3;
|
||||
}
|
||||
|
||||
// This provides the CRUD+List+Watch support needed for a k8s apiserver
|
||||
// The semantics and behaviors of this service are constrained by kubernetes
|
||||
// This does not understand the resource schemas, only deals with json bytes
|
||||
@@ -631,3 +647,8 @@ service Diagnostics {
|
||||
// Check if the service is healthy
|
||||
rpc IsHealthy(HealthCheckRequest) returns (HealthCheckResponse);
|
||||
}
|
||||
|
||||
service Quotas {
|
||||
// Get current quota usage and limits
|
||||
rpc GetQuotaUsage(QuotaUsageRequest) returns (QuotaUsageResponse);
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ type ResourceClient interface {
|
||||
resourcepb.BulkStoreClient
|
||||
resourcepb.BlobStoreClient
|
||||
resourcepb.DiagnosticsClient
|
||||
resourcepb.QuotasClient
|
||||
}
|
||||
|
||||
// Internal implementation
|
||||
@@ -49,6 +50,7 @@ type resourceClient struct {
|
||||
resourcepb.BulkStoreClient
|
||||
resourcepb.BlobStoreClient
|
||||
resourcepb.DiagnosticsClient
|
||||
resourcepb.QuotasClient
|
||||
}
|
||||
|
||||
func NewResourceClient(conn, indexConn grpc.ClientConnInterface, cfg *setting.Cfg, features featuremgmt.FeatureToggles, tracer trace.Tracer) (ResourceClient, error) {
|
||||
@@ -76,6 +78,7 @@ func newResourceClient(storageCc grpc.ClientConnInterface, indexCc grpc.ClientCo
|
||||
BulkStoreClient: resourcepb.NewBulkStoreClient(storageCc),
|
||||
BlobStoreClient: resourcepb.NewBlobStoreClient(storageCc),
|
||||
DiagnosticsClient: resourcepb.NewDiagnosticsClient(storageCc),
|
||||
QuotasClient: resourcepb.NewQuotasClient(storageCc),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,6 +105,7 @@ func NewLocalResourceClient(server ResourceServer) ResourceClient {
|
||||
&resourcepb.BlobStore_ServiceDesc,
|
||||
&resourcepb.BulkStore_ServiceDesc,
|
||||
&resourcepb.Diagnostics_ServiceDesc,
|
||||
&resourcepb.Quotas_ServiceDesc,
|
||||
} {
|
||||
channel.RegisterService(
|
||||
grpchan.InterceptServer(
|
||||
|
||||
@@ -394,6 +394,80 @@ func (_c *MockResourceClient_GetBlob_Call) RunAndReturn(run func(context.Context
|
||||
return _c
|
||||
}
|
||||
|
||||
// GetQuotaUsage provides a mock function with given fields: ctx, in, opts
|
||||
func (_m *MockResourceClient) GetQuotaUsage(ctx context.Context, in *resourcepb.QuotaUsageRequest, opts ...grpc.CallOption) (*resourcepb.QuotaUsageResponse, error) {
|
||||
_va := make([]interface{}, len(opts))
|
||||
for _i := range opts {
|
||||
_va[_i] = opts[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ctx, in)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetQuotaUsage")
|
||||
}
|
||||
|
||||
var r0 *resourcepb.QuotaUsageResponse
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *resourcepb.QuotaUsageRequest, ...grpc.CallOption) (*resourcepb.QuotaUsageResponse, error)); ok {
|
||||
return rf(ctx, in, opts...)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *resourcepb.QuotaUsageRequest, ...grpc.CallOption) *resourcepb.QuotaUsageResponse); ok {
|
||||
r0 = rf(ctx, in, opts...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*resourcepb.QuotaUsageResponse)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *resourcepb.QuotaUsageRequest, ...grpc.CallOption) error); ok {
|
||||
r1 = rf(ctx, in, opts...)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// MockResourceClient_GetQuotaUsage_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetQuotaUsage'
|
||||
type MockResourceClient_GetQuotaUsage_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// GetQuotaUsage is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
// - in *resourcepb.QuotaUsageRequest
|
||||
// - opts ...grpc.CallOption
|
||||
func (_e *MockResourceClient_Expecter) GetQuotaUsage(ctx interface{}, in interface{}, opts ...interface{}) *MockResourceClient_GetQuotaUsage_Call {
|
||||
return &MockResourceClient_GetQuotaUsage_Call{Call: _e.mock.On("GetQuotaUsage",
|
||||
append([]interface{}{ctx, in}, opts...)...)}
|
||||
}
|
||||
|
||||
func (_c *MockResourceClient_GetQuotaUsage_Call) Run(run func(ctx context.Context, in *resourcepb.QuotaUsageRequest, opts ...grpc.CallOption)) *MockResourceClient_GetQuotaUsage_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
variadicArgs := make([]grpc.CallOption, len(args)-2)
|
||||
for i, a := range args[2:] {
|
||||
if a != nil {
|
||||
variadicArgs[i] = a.(grpc.CallOption)
|
||||
}
|
||||
}
|
||||
run(args[0].(context.Context), args[1].(*resourcepb.QuotaUsageRequest), variadicArgs...)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockResourceClient_GetQuotaUsage_Call) Return(_a0 *resourcepb.QuotaUsageResponse, _a1 error) *MockResourceClient_GetQuotaUsage_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockResourceClient_GetQuotaUsage_Call) RunAndReturn(run func(context.Context, *resourcepb.QuotaUsageRequest, ...grpc.CallOption) (*resourcepb.QuotaUsageResponse, error)) *MockResourceClient_GetQuotaUsage_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// GetStats provides a mock function with given fields: ctx, in, opts
|
||||
func (_m *MockResourceClient) GetStats(ctx context.Context, in *resourcepb.ResourceStatsRequest, opts ...grpc.CallOption) (*resourcepb.ResourceStatsResponse, error) {
|
||||
_va := make([]interface{}, len(opts))
|
||||
|
||||
@@ -41,6 +41,7 @@ type ResourceServer interface {
|
||||
resourcepb.ManagedObjectIndexServer
|
||||
resourcepb.BlobStoreServer
|
||||
resourcepb.DiagnosticsServer
|
||||
resourcepb.QuotasServer
|
||||
}
|
||||
|
||||
type ListIterator interface {
|
||||
@@ -1466,6 +1467,38 @@ func (s *server) PutBlob(ctx context.Context, req *resourcepb.PutBlobRequest) (*
|
||||
return rsp, nil
|
||||
}
|
||||
|
||||
func (s *server) GetQuotaUsage(ctx context.Context, req *resourcepb.QuotaUsageRequest) (*resourcepb.QuotaUsageResponse, error) {
|
||||
if s.overridesService == nil {
|
||||
return &resourcepb.QuotaUsageResponse{Error: &resourcepb.ErrorResult{
|
||||
Message: "overrides service not configured on resource server",
|
||||
Code: http.StatusNotImplemented,
|
||||
}}, nil
|
||||
}
|
||||
nsr := NamespacedResource{
|
||||
Namespace: req.Key.Namespace,
|
||||
Group: req.Key.Group,
|
||||
Resource: req.Key.Resource,
|
||||
}
|
||||
usage, err := s.backend.GetResourceStats(ctx, nsr, 0)
|
||||
if err != nil {
|
||||
return &resourcepb.QuotaUsageResponse{Error: AsErrorResult(err)}, nil
|
||||
}
|
||||
limit, err := s.overridesService.GetQuota(ctx, nsr)
|
||||
if err != nil {
|
||||
return &resourcepb.QuotaUsageResponse{Error: AsErrorResult(err)}, nil
|
||||
}
|
||||
|
||||
// handle case where no resources exist yet - very unlikely but possible
|
||||
rsp := &resourcepb.QuotaUsageResponse{Limit: int64(limit.Limit)}
|
||||
if len(usage) <= 0 {
|
||||
rsp.Usage = 0
|
||||
} else {
|
||||
rsp.Usage = usage[0].Count
|
||||
}
|
||||
|
||||
return rsp, nil
|
||||
}
|
||||
|
||||
func (s *server) getPartialObject(ctx context.Context, key *resourcepb.ResourceKey, rv int64) (utils.GrafanaMetaAccessor, *resourcepb.ErrorResult) {
|
||||
if r := verifyRequestKey(key); r != nil {
|
||||
return nil, r
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
@@ -22,6 +24,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
"github.com/grafana/grafana/pkg/util/scheduler"
|
||||
)
|
||||
@@ -614,3 +617,70 @@ func TestArtificialDelayAfterSuccessfulOperation(t *testing.T) {
|
||||
check(t, false, &resourcepb.UpdateResponse{Error: AsErrorResult(errors.New("some error"))}, nil)
|
||||
check(t, false, &resourcepb.DeleteResponse{Error: AsErrorResult(errors.New("some error"))}, nil)
|
||||
}
|
||||
|
||||
func TestGetQuotaUsage(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("returns error when overrides service is not configured", func(t *testing.T) {
|
||||
s := &server{
|
||||
overridesService: nil,
|
||||
log: log.NewNopLogger(),
|
||||
}
|
||||
|
||||
resp, err := s.GetQuotaUsage(ctx, &resourcepb.QuotaUsageRequest{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: "stacks-123",
|
||||
Group: "dashboard.grafana.app",
|
||||
Resource: "dashboards",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, resp.Error)
|
||||
assert.Equal(t, int32(http.StatusNotImplemented), resp.Error.Code)
|
||||
assert.Equal(t, "overrides service not configured on resource server", resp.Error.Message)
|
||||
})
|
||||
|
||||
t.Run("returns usage and limit successfully", func(t *testing.T) {
|
||||
// Create a temporary overrides config file
|
||||
tmpFile := filepath.Join(t.TempDir(), "overrides.yaml")
|
||||
content := `"123":
|
||||
quotas:
|
||||
dashboard.grafana.app/dashboards:
|
||||
limit: 500
|
||||
`
|
||||
require.NoError(t, os.WriteFile(tmpFile, []byte(content), 0644))
|
||||
|
||||
// Create a real OverridesService with the temp file
|
||||
overridesService, err := NewOverridesService(ctx, log.NewNopLogger(), prometheus.NewRegistry(), tracing.NewNoopTracerService(), ReloadOptions{
|
||||
FilePath: tmpFile,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, overridesService.init(ctx))
|
||||
defer func() {
|
||||
_ = overridesService.stop(ctx)
|
||||
}()
|
||||
|
||||
// Create a mock backend that returns resource stats (reusing mockStorageBackend from search_test.go)
|
||||
mockBackend := &mockStorageBackend{
|
||||
resourceStats: []ResourceStats{{Count: 42}},
|
||||
}
|
||||
|
||||
s := &server{
|
||||
backend: mockBackend,
|
||||
overridesService: overridesService,
|
||||
log: log.NewNopLogger(),
|
||||
}
|
||||
|
||||
resp, err := s.GetQuotaUsage(ctx, &resourcepb.QuotaUsageRequest{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: "stacks-123",
|
||||
Group: "dashboard.grafana.app",
|
||||
Resource: "dashboards",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, resp.Error)
|
||||
assert.Equal(t, int64(42), resp.Usage)
|
||||
assert.Equal(t, int64(500), resp.Limit)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2484,6 +2484,114 @@ func (x *ResourceTableRow) GetObject() []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
type QuotaUsageRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// Namespace (tenant)
|
||||
Key *ResourceKey `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *QuotaUsageRequest) Reset() {
|
||||
*x = QuotaUsageRequest{}
|
||||
mi := &file_resource_proto_msgTypes[30]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *QuotaUsageRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*QuotaUsageRequest) ProtoMessage() {}
|
||||
|
||||
func (x *QuotaUsageRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_resource_proto_msgTypes[30]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use QuotaUsageRequest.ProtoReflect.Descriptor instead.
|
||||
func (*QuotaUsageRequest) Descriptor() ([]byte, []int) {
|
||||
return file_resource_proto_rawDescGZIP(), []int{30}
|
||||
}
|
||||
|
||||
func (x *QuotaUsageRequest) GetKey() *ResourceKey {
|
||||
if x != nil {
|
||||
return x.Key
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type QuotaUsageResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// Error details
|
||||
Error *ErrorResult `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"`
|
||||
// Current usage
|
||||
Usage int64 `protobuf:"varint,2,opt,name=usage,proto3" json:"usage,omitempty"`
|
||||
// Current limit
|
||||
Limit int64 `protobuf:"varint,3,opt,name=limit,proto3" json:"limit,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *QuotaUsageResponse) Reset() {
|
||||
*x = QuotaUsageResponse{}
|
||||
mi := &file_resource_proto_msgTypes[31]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *QuotaUsageResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*QuotaUsageResponse) ProtoMessage() {}
|
||||
|
||||
func (x *QuotaUsageResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_resource_proto_msgTypes[31]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use QuotaUsageResponse.ProtoReflect.Descriptor instead.
|
||||
func (*QuotaUsageResponse) Descriptor() ([]byte, []int) {
|
||||
return file_resource_proto_rawDescGZIP(), []int{31}
|
||||
}
|
||||
|
||||
func (x *QuotaUsageResponse) GetError() *ErrorResult {
|
||||
if x != nil {
|
||||
return x.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *QuotaUsageResponse) GetUsage() int64 {
|
||||
if x != nil {
|
||||
return x.Usage
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *QuotaUsageResponse) GetLimit() int64 {
|
||||
if x != nil {
|
||||
return x.Limit
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type WatchEvent_Resource struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Version int64 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"`
|
||||
@@ -2494,7 +2602,7 @@ type WatchEvent_Resource struct {
|
||||
|
||||
func (x *WatchEvent_Resource) Reset() {
|
||||
*x = WatchEvent_Resource{}
|
||||
mi := &file_resource_proto_msgTypes[30]
|
||||
mi := &file_resource_proto_msgTypes[32]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -2506,7 +2614,7 @@ func (x *WatchEvent_Resource) String() string {
|
||||
func (*WatchEvent_Resource) ProtoMessage() {}
|
||||
|
||||
func (x *WatchEvent_Resource) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_resource_proto_msgTypes[30]
|
||||
mi := &file_resource_proto_msgTypes[32]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -2553,7 +2661,7 @@ type BulkResponse_Summary struct {
|
||||
|
||||
func (x *BulkResponse_Summary) Reset() {
|
||||
*x = BulkResponse_Summary{}
|
||||
mi := &file_resource_proto_msgTypes[31]
|
||||
mi := &file_resource_proto_msgTypes[33]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -2565,7 +2673,7 @@ func (x *BulkResponse_Summary) String() string {
|
||||
func (*BulkResponse_Summary) ProtoMessage() {}
|
||||
|
||||
func (x *BulkResponse_Summary) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_resource_proto_msgTypes[31]
|
||||
mi := &file_resource_proto_msgTypes[33]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -2649,7 +2757,7 @@ type BulkResponse_Rejected struct {
|
||||
|
||||
func (x *BulkResponse_Rejected) Reset() {
|
||||
*x = BulkResponse_Rejected{}
|
||||
mi := &file_resource_proto_msgTypes[32]
|
||||
mi := &file_resource_proto_msgTypes[34]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -2661,7 +2769,7 @@ func (x *BulkResponse_Rejected) String() string {
|
||||
func (*BulkResponse_Rejected) ProtoMessage() {}
|
||||
|
||||
func (x *BulkResponse_Rejected) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_resource_proto_msgTypes[32]
|
||||
mi := &file_resource_proto_msgTypes[34]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -2718,7 +2826,7 @@ type ListManagedObjectsResponse_Item struct {
|
||||
|
||||
func (x *ListManagedObjectsResponse_Item) Reset() {
|
||||
*x = ListManagedObjectsResponse_Item{}
|
||||
mi := &file_resource_proto_msgTypes[33]
|
||||
mi := &file_resource_proto_msgTypes[35]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -2730,7 +2838,7 @@ func (x *ListManagedObjectsResponse_Item) String() string {
|
||||
func (*ListManagedObjectsResponse_Item) ProtoMessage() {}
|
||||
|
||||
func (x *ListManagedObjectsResponse_Item) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_resource_proto_msgTypes[33]
|
||||
mi := &file_resource_proto_msgTypes[35]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -2801,7 +2909,7 @@ type CountManagedObjectsResponse_ResourceCount struct {
|
||||
|
||||
func (x *CountManagedObjectsResponse_ResourceCount) Reset() {
|
||||
*x = CountManagedObjectsResponse_ResourceCount{}
|
||||
mi := &file_resource_proto_msgTypes[34]
|
||||
mi := &file_resource_proto_msgTypes[36]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -2813,7 +2921,7 @@ func (x *CountManagedObjectsResponse_ResourceCount) String() string {
|
||||
func (*CountManagedObjectsResponse_ResourceCount) ProtoMessage() {}
|
||||
|
||||
func (x *CountManagedObjectsResponse_ResourceCount) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_resource_proto_msgTypes[34]
|
||||
mi := &file_resource_proto_msgTypes[36]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -2886,7 +2994,7 @@ type ResourceTableColumnDefinition_Properties struct {
|
||||
|
||||
func (x *ResourceTableColumnDefinition_Properties) Reset() {
|
||||
*x = ResourceTableColumnDefinition_Properties{}
|
||||
mi := &file_resource_proto_msgTypes[35]
|
||||
mi := &file_resource_proto_msgTypes[37]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -2898,7 +3006,7 @@ func (x *ResourceTableColumnDefinition_Properties) String() string {
|
||||
func (*ResourceTableColumnDefinition_Properties) ProtoMessage() {}
|
||||
|
||||
func (x *ResourceTableColumnDefinition_Properties) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_resource_proto_msgTypes[35]
|
||||
mi := &file_resource_proto_msgTypes[37]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -3320,68 +3428,85 @@ var file_resource_proto_rawDesc = string([]byte{
|
||||
0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
|
||||
0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52,
|
||||
0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74,
|
||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2a, 0x49,
|
||||
0x0a, 0x14, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x12, 0x1b, 0x0a, 0x17, 0x44, 0x45, 0x50, 0x52, 0x45, 0x43,
|
||||
0x41, 0x54, 0x45, 0x44, 0x5f, 0x4e, 0x6f, 0x74, 0x4f, 0x6c, 0x64, 0x65, 0x72, 0x54, 0x68, 0x61,
|
||||
0x6e, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x44, 0x45, 0x50, 0x52, 0x45, 0x43, 0x41, 0x54, 0x45,
|
||||
0x44, 0x5f, 0x45, 0x78, 0x61, 0x63, 0x74, 0x10, 0x01, 0x2a, 0x4d, 0x0a, 0x16, 0x52, 0x65, 0x73,
|
||||
0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x74, 0x63,
|
||||
0x68, 0x56, 0x32, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00,
|
||||
0x12, 0x09, 0x0a, 0x05, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x45,
|
||||
0x78, 0x61, 0x63, 0x74, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x4f, 0x6c, 0x64,
|
||||
0x65, 0x72, 0x54, 0x68, 0x61, 0x6e, 0x10, 0x03, 0x32, 0xed, 0x02, 0x0a, 0x0d, 0x52, 0x65, 0x73,
|
||||
0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x35, 0x0a, 0x04, 0x52, 0x65,
|
||||
0x61, 0x64, 0x12, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65,
|
||||
0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x72, 0x65, 0x73, 0x6f,
|
||||
0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x12, 0x3b, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x72, 0x65,
|
||||
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e,
|
||||
0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b,
|
||||
0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75,
|
||||
0x72, 0x63, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x1a, 0x18, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x55, 0x70, 0x64,
|
||||
0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x06, 0x44,
|
||||
0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
|
||||
0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18,
|
||||
0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74,
|
||||
0x12, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72,
|
||||
0x63, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
|
||||
0x37, 0x0a, 0x05, 0x57, 0x61, 0x74, 0x63, 0x68, 0x12, 0x16, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75,
|
||||
0x72, 0x63, 0x65, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x1a, 0x14, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x57, 0x61, 0x74, 0x63,
|
||||
0x68, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x32, 0x4b, 0x0a, 0x09, 0x42, 0x75, 0x6c, 0x6b,
|
||||
0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x3e, 0x0a, 0x0b, 0x42, 0x75, 0x6c, 0x6b, 0x50, 0x72, 0x6f,
|
||||
0x63, 0x65, 0x73, 0x73, 0x12, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e,
|
||||
0x42, 0x75, 0x6c, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x72, 0x65,
|
||||
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x42, 0x75, 0x6c, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x28, 0x01, 0x32, 0xd9, 0x01, 0x0a, 0x12, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65,
|
||||
0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x62, 0x0a, 0x13,
|
||||
0x43, 0x6f, 0x75, 0x6e, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65,
|
||||
0x63, 0x74, 0x73, 0x12, 0x24, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x43,
|
||||
0x6f, 0x75, 0x6e, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63,
|
||||
0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x72, 0x65, 0x73, 0x6f,
|
||||
0x75, 0x72, 0x63, 0x65, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65,
|
||||
0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x12, 0x5f, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x4f,
|
||||
0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x23, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
|
||||
0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x4f, 0x62, 0x6a,
|
||||
0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x72, 0x65,
|
||||
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67,
|
||||
0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x32, 0x57, 0x0a, 0x0b, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73,
|
||||
0x12, 0x48, 0x0a, 0x09, 0x49, 0x73, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x12, 0x1c, 0x2e,
|
||||
0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43,
|
||||
0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x72, 0x65,
|
||||
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65,
|
||||
0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69,
|
||||
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x66, 0x61, 0x6e, 0x61,
|
||||
0x2f, 0x67, 0x72, 0x61, 0x66, 0x61, 0x6e, 0x61, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x73, 0x74, 0x6f,
|
||||
0x72, 0x61, 0x67, 0x65, 0x2f, 0x75, 0x6e, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2f, 0x72, 0x65, 0x73,
|
||||
0x6f, 0x75, 0x72, 0x63, 0x65, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x3c,
|
||||
0x0a, 0x11, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f,
|
||||
0x75, 0x72, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x6d, 0x0a, 0x12,
|
||||
0x51, 0x75, 0x6f, 0x74, 0x61, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x45, 0x72, 0x72,
|
||||
0x6f, 0x72, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12,
|
||||
0x14, 0x0a, 0x05, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05,
|
||||
0x75, 0x73, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03,
|
||||
0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x2a, 0x49, 0x0a, 0x14, 0x52,
|
||||
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x61,
|
||||
0x74, 0x63, 0x68, 0x12, 0x1b, 0x0a, 0x17, 0x44, 0x45, 0x50, 0x52, 0x45, 0x43, 0x41, 0x54, 0x45,
|
||||
0x44, 0x5f, 0x4e, 0x6f, 0x74, 0x4f, 0x6c, 0x64, 0x65, 0x72, 0x54, 0x68, 0x61, 0x6e, 0x10, 0x00,
|
||||
0x12, 0x14, 0x0a, 0x10, 0x44, 0x45, 0x50, 0x52, 0x45, 0x43, 0x41, 0x54, 0x45, 0x44, 0x5f, 0x45,
|
||||
0x78, 0x61, 0x63, 0x74, 0x10, 0x01, 0x2a, 0x4d, 0x0a, 0x16, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72,
|
||||
0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x56, 0x32,
|
||||
0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a,
|
||||
0x05, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x78, 0x61, 0x63,
|
||||
0x74, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x4f, 0x6c, 0x64, 0x65, 0x72, 0x54,
|
||||
0x68, 0x61, 0x6e, 0x10, 0x03, 0x32, 0xed, 0x02, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72,
|
||||
0x63, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x35, 0x0a, 0x04, 0x52, 0x65, 0x61, 0x64, 0x12,
|
||||
0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
|
||||
0x65, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b,
|
||||
0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75,
|
||||
0x72, 0x63, 0x65, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x1a, 0x18, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x43, 0x72, 0x65,
|
||||
0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x06, 0x55,
|
||||
0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
|
||||
0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18,
|
||||
0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65,
|
||||
0x74, 0x65, 0x12, 0x17, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x44, 0x65,
|
||||
0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x72, 0x65,
|
||||
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x15, 0x2e,
|
||||
0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e,
|
||||
0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x05,
|
||||
0x57, 0x61, 0x74, 0x63, 0x68, 0x12, 0x16, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
|
||||
0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e,
|
||||
0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, 0x45, 0x76,
|
||||
0x65, 0x6e, 0x74, 0x30, 0x01, 0x32, 0x4b, 0x0a, 0x09, 0x42, 0x75, 0x6c, 0x6b, 0x53, 0x74, 0x6f,
|
||||
0x72, 0x65, 0x12, 0x3e, 0x0a, 0x0b, 0x42, 0x75, 0x6c, 0x6b, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73,
|
||||
0x73, 0x12, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x42, 0x75, 0x6c,
|
||||
0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75,
|
||||
0x72, 0x63, 0x65, 0x2e, 0x42, 0x75, 0x6c, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x28, 0x01, 0x32, 0xd9, 0x01, 0x0a, 0x12, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x4f, 0x62,
|
||||
0x6a, 0x65, 0x63, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x62, 0x0a, 0x13, 0x43, 0x6f, 0x75,
|
||||
0x6e, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73,
|
||||
0x12, 0x24, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x43, 0x6f, 0x75, 0x6e,
|
||||
0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
|
||||
0x65, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x4f, 0x62,
|
||||
0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a,
|
||||
0x12, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65,
|
||||
0x63, 0x74, 0x73, 0x12, 0x23, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4c,
|
||||
0x69, 0x73, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74,
|
||||
0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75,
|
||||
0x72, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x4f,
|
||||
0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x57,
|
||||
0x0a, 0x0b, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x48, 0x0a,
|
||||
0x09, 0x49, 0x73, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x12, 0x1c, 0x2e, 0x72, 0x65, 0x73,
|
||||
0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63,
|
||||
0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75,
|
||||
0x72, 0x63, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x54, 0x0a, 0x06, 0x51, 0x75, 0x6f, 0x74, 0x61,
|
||||
0x73, 0x12, 0x4a, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x55, 0x73, 0x61,
|
||||
0x67, 0x65, 0x12, 0x1b, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x51, 0x75,
|
||||
0x6f, 0x74, 0x61, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||
0x1c, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x51, 0x75, 0x6f, 0x74, 0x61,
|
||||
0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3b, 0x5a,
|
||||
0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x66,
|
||||
0x61, 0x6e, 0x61, 0x2f, 0x67, 0x72, 0x61, 0x66, 0x61, 0x6e, 0x61, 0x2f, 0x70, 0x6b, 0x67, 0x2f,
|
||||
0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x75, 0x6e, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2f,
|
||||
0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x33,
|
||||
})
|
||||
|
||||
var (
|
||||
@@ -3397,7 +3522,7 @@ func file_resource_proto_rawDescGZIP() []byte {
|
||||
}
|
||||
|
||||
var file_resource_proto_enumTypes = make([]protoimpl.EnumInfo, 7)
|
||||
var file_resource_proto_msgTypes = make([]protoimpl.MessageInfo, 36)
|
||||
var file_resource_proto_msgTypes = make([]protoimpl.MessageInfo, 38)
|
||||
var file_resource_proto_goTypes = []any{
|
||||
(ResourceVersionMatch)(0), // 0: resource.ResourceVersionMatch
|
||||
(ResourceVersionMatchV2)(0), // 1: resource.ResourceVersionMatchV2
|
||||
@@ -3436,12 +3561,14 @@ var file_resource_proto_goTypes = []any{
|
||||
(*ResourceTable)(nil), // 34: resource.ResourceTable
|
||||
(*ResourceTableColumnDefinition)(nil), // 35: resource.ResourceTableColumnDefinition
|
||||
(*ResourceTableRow)(nil), // 36: resource.ResourceTableRow
|
||||
(*WatchEvent_Resource)(nil), // 37: resource.WatchEvent.Resource
|
||||
(*BulkResponse_Summary)(nil), // 38: resource.BulkResponse.Summary
|
||||
(*BulkResponse_Rejected)(nil), // 39: resource.BulkResponse.Rejected
|
||||
(*ListManagedObjectsResponse_Item)(nil), // 40: resource.ListManagedObjectsResponse.Item
|
||||
(*CountManagedObjectsResponse_ResourceCount)(nil), // 41: resource.CountManagedObjectsResponse.ResourceCount
|
||||
(*ResourceTableColumnDefinition_Properties)(nil), // 42: resource.ResourceTableColumnDefinition.Properties
|
||||
(*QuotaUsageRequest)(nil), // 37: resource.QuotaUsageRequest
|
||||
(*QuotaUsageResponse)(nil), // 38: resource.QuotaUsageResponse
|
||||
(*WatchEvent_Resource)(nil), // 39: resource.WatchEvent.Resource
|
||||
(*BulkResponse_Summary)(nil), // 40: resource.BulkResponse.Summary
|
||||
(*BulkResponse_Rejected)(nil), // 41: resource.BulkResponse.Rejected
|
||||
(*ListManagedObjectsResponse_Item)(nil), // 42: resource.ListManagedObjectsResponse.Item
|
||||
(*CountManagedObjectsResponse_ResourceCount)(nil), // 43: resource.CountManagedObjectsResponse.ResourceCount
|
||||
(*ResourceTableColumnDefinition_Properties)(nil), // 44: resource.ResourceTableColumnDefinition.Properties
|
||||
}
|
||||
var file_resource_proto_depIdxs = []int32{
|
||||
10, // 0: resource.ErrorResult.details:type_name -> resource.ErrorDetails
|
||||
@@ -3465,51 +3592,55 @@ var file_resource_proto_depIdxs = []int32{
|
||||
9, // 18: resource.ListResponse.error:type_name -> resource.ErrorResult
|
||||
21, // 19: resource.WatchRequest.options:type_name -> resource.ListOptions
|
||||
3, // 20: resource.WatchEvent.type:type_name -> resource.WatchEvent.Type
|
||||
37, // 21: resource.WatchEvent.resource:type_name -> resource.WatchEvent.Resource
|
||||
37, // 22: resource.WatchEvent.previous:type_name -> resource.WatchEvent.Resource
|
||||
39, // 21: resource.WatchEvent.resource:type_name -> resource.WatchEvent.Resource
|
||||
39, // 22: resource.WatchEvent.previous:type_name -> resource.WatchEvent.Resource
|
||||
7, // 23: resource.BulkRequest.key:type_name -> resource.ResourceKey
|
||||
4, // 24: resource.BulkRequest.action:type_name -> resource.BulkRequest.Action
|
||||
9, // 25: resource.BulkResponse.error:type_name -> resource.ErrorResult
|
||||
38, // 26: resource.BulkResponse.summary:type_name -> resource.BulkResponse.Summary
|
||||
39, // 27: resource.BulkResponse.rejected:type_name -> resource.BulkResponse.Rejected
|
||||
40, // 28: resource.ListManagedObjectsResponse.items:type_name -> resource.ListManagedObjectsResponse.Item
|
||||
40, // 26: resource.BulkResponse.summary:type_name -> resource.BulkResponse.Summary
|
||||
41, // 27: resource.BulkResponse.rejected:type_name -> resource.BulkResponse.Rejected
|
||||
42, // 28: resource.ListManagedObjectsResponse.items:type_name -> resource.ListManagedObjectsResponse.Item
|
||||
9, // 29: resource.ListManagedObjectsResponse.error:type_name -> resource.ErrorResult
|
||||
41, // 30: resource.CountManagedObjectsResponse.items:type_name -> resource.CountManagedObjectsResponse.ResourceCount
|
||||
43, // 30: resource.CountManagedObjectsResponse.items:type_name -> resource.CountManagedObjectsResponse.ResourceCount
|
||||
9, // 31: resource.CountManagedObjectsResponse.error:type_name -> resource.ErrorResult
|
||||
5, // 32: resource.HealthCheckResponse.status:type_name -> resource.HealthCheckResponse.ServingStatus
|
||||
35, // 33: resource.ResourceTable.columns:type_name -> resource.ResourceTableColumnDefinition
|
||||
36, // 34: resource.ResourceTable.rows:type_name -> resource.ResourceTableRow
|
||||
6, // 35: resource.ResourceTableColumnDefinition.type:type_name -> resource.ResourceTableColumnDefinition.ColumnType
|
||||
42, // 36: resource.ResourceTableColumnDefinition.properties:type_name -> resource.ResourceTableColumnDefinition.Properties
|
||||
44, // 36: resource.ResourceTableColumnDefinition.properties:type_name -> resource.ResourceTableColumnDefinition.Properties
|
||||
7, // 37: resource.ResourceTableRow.key:type_name -> resource.ResourceKey
|
||||
7, // 38: resource.BulkResponse.Rejected.key:type_name -> resource.ResourceKey
|
||||
4, // 39: resource.BulkResponse.Rejected.action:type_name -> resource.BulkRequest.Action
|
||||
7, // 40: resource.ListManagedObjectsResponse.Item.object:type_name -> resource.ResourceKey
|
||||
18, // 41: resource.ResourceStore.Read:input_type -> resource.ReadRequest
|
||||
12, // 42: resource.ResourceStore.Create:input_type -> resource.CreateRequest
|
||||
14, // 43: resource.ResourceStore.Update:input_type -> resource.UpdateRequest
|
||||
16, // 44: resource.ResourceStore.Delete:input_type -> resource.DeleteRequest
|
||||
22, // 45: resource.ResourceStore.List:input_type -> resource.ListRequest
|
||||
24, // 46: resource.ResourceStore.Watch:input_type -> resource.WatchRequest
|
||||
26, // 47: resource.BulkStore.BulkProcess:input_type -> resource.BulkRequest
|
||||
30, // 48: resource.ManagedObjectIndex.CountManagedObjects:input_type -> resource.CountManagedObjectsRequest
|
||||
28, // 49: resource.ManagedObjectIndex.ListManagedObjects:input_type -> resource.ListManagedObjectsRequest
|
||||
32, // 50: resource.Diagnostics.IsHealthy:input_type -> resource.HealthCheckRequest
|
||||
19, // 51: resource.ResourceStore.Read:output_type -> resource.ReadResponse
|
||||
13, // 52: resource.ResourceStore.Create:output_type -> resource.CreateResponse
|
||||
15, // 53: resource.ResourceStore.Update:output_type -> resource.UpdateResponse
|
||||
17, // 54: resource.ResourceStore.Delete:output_type -> resource.DeleteResponse
|
||||
23, // 55: resource.ResourceStore.List:output_type -> resource.ListResponse
|
||||
25, // 56: resource.ResourceStore.Watch:output_type -> resource.WatchEvent
|
||||
27, // 57: resource.BulkStore.BulkProcess:output_type -> resource.BulkResponse
|
||||
31, // 58: resource.ManagedObjectIndex.CountManagedObjects:output_type -> resource.CountManagedObjectsResponse
|
||||
29, // 59: resource.ManagedObjectIndex.ListManagedObjects:output_type -> resource.ListManagedObjectsResponse
|
||||
33, // 60: resource.Diagnostics.IsHealthy:output_type -> resource.HealthCheckResponse
|
||||
51, // [51:61] is the sub-list for method output_type
|
||||
41, // [41:51] is the sub-list for method input_type
|
||||
41, // [41:41] is the sub-list for extension type_name
|
||||
41, // [41:41] is the sub-list for extension extendee
|
||||
0, // [0:41] is the sub-list for field type_name
|
||||
7, // 38: resource.QuotaUsageRequest.key:type_name -> resource.ResourceKey
|
||||
9, // 39: resource.QuotaUsageResponse.error:type_name -> resource.ErrorResult
|
||||
7, // 40: resource.BulkResponse.Rejected.key:type_name -> resource.ResourceKey
|
||||
4, // 41: resource.BulkResponse.Rejected.action:type_name -> resource.BulkRequest.Action
|
||||
7, // 42: resource.ListManagedObjectsResponse.Item.object:type_name -> resource.ResourceKey
|
||||
18, // 43: resource.ResourceStore.Read:input_type -> resource.ReadRequest
|
||||
12, // 44: resource.ResourceStore.Create:input_type -> resource.CreateRequest
|
||||
14, // 45: resource.ResourceStore.Update:input_type -> resource.UpdateRequest
|
||||
16, // 46: resource.ResourceStore.Delete:input_type -> resource.DeleteRequest
|
||||
22, // 47: resource.ResourceStore.List:input_type -> resource.ListRequest
|
||||
24, // 48: resource.ResourceStore.Watch:input_type -> resource.WatchRequest
|
||||
26, // 49: resource.BulkStore.BulkProcess:input_type -> resource.BulkRequest
|
||||
30, // 50: resource.ManagedObjectIndex.CountManagedObjects:input_type -> resource.CountManagedObjectsRequest
|
||||
28, // 51: resource.ManagedObjectIndex.ListManagedObjects:input_type -> resource.ListManagedObjectsRequest
|
||||
32, // 52: resource.Diagnostics.IsHealthy:input_type -> resource.HealthCheckRequest
|
||||
37, // 53: resource.Quotas.GetQuotaUsage:input_type -> resource.QuotaUsageRequest
|
||||
19, // 54: resource.ResourceStore.Read:output_type -> resource.ReadResponse
|
||||
13, // 55: resource.ResourceStore.Create:output_type -> resource.CreateResponse
|
||||
15, // 56: resource.ResourceStore.Update:output_type -> resource.UpdateResponse
|
||||
17, // 57: resource.ResourceStore.Delete:output_type -> resource.DeleteResponse
|
||||
23, // 58: resource.ResourceStore.List:output_type -> resource.ListResponse
|
||||
25, // 59: resource.ResourceStore.Watch:output_type -> resource.WatchEvent
|
||||
27, // 60: resource.BulkStore.BulkProcess:output_type -> resource.BulkResponse
|
||||
31, // 61: resource.ManagedObjectIndex.CountManagedObjects:output_type -> resource.CountManagedObjectsResponse
|
||||
29, // 62: resource.ManagedObjectIndex.ListManagedObjects:output_type -> resource.ListManagedObjectsResponse
|
||||
33, // 63: resource.Diagnostics.IsHealthy:output_type -> resource.HealthCheckResponse
|
||||
38, // 64: resource.Quotas.GetQuotaUsage:output_type -> resource.QuotaUsageResponse
|
||||
54, // [54:65] is the sub-list for method output_type
|
||||
43, // [43:54] is the sub-list for method input_type
|
||||
43, // [43:43] is the sub-list for extension type_name
|
||||
43, // [43:43] is the sub-list for extension extendee
|
||||
0, // [0:43] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_resource_proto_init() }
|
||||
@@ -3524,9 +3655,9 @@ func file_resource_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_resource_proto_rawDesc), len(file_resource_proto_rawDesc)),
|
||||
NumEnums: 7,
|
||||
NumMessages: 36,
|
||||
NumMessages: 38,
|
||||
NumExtensions: 0,
|
||||
NumServices: 4,
|
||||
NumServices: 5,
|
||||
},
|
||||
GoTypes: file_resource_proto_goTypes,
|
||||
DependencyIndexes: file_resource_proto_depIdxs,
|
||||
|
||||
@@ -709,3 +709,94 @@ var Diagnostics_ServiceDesc = grpc.ServiceDesc{
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "resource.proto",
|
||||
}
|
||||
|
||||
const (
|
||||
Quotas_GetQuotaUsage_FullMethodName = "/resource.Quotas/GetQuotaUsage"
|
||||
)
|
||||
|
||||
// QuotasClient is the client API for Quotas service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type QuotasClient interface {
|
||||
// Get current quota usage and limits
|
||||
GetQuotaUsage(ctx context.Context, in *QuotaUsageRequest, opts ...grpc.CallOption) (*QuotaUsageResponse, error)
|
||||
}
|
||||
|
||||
type quotasClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewQuotasClient(cc grpc.ClientConnInterface) QuotasClient {
|
||||
return "asClient{cc}
|
||||
}
|
||||
|
||||
func (c *quotasClient) GetQuotaUsage(ctx context.Context, in *QuotaUsageRequest, opts ...grpc.CallOption) (*QuotaUsageResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(QuotaUsageResponse)
|
||||
err := c.cc.Invoke(ctx, Quotas_GetQuotaUsage_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// QuotasServer is the server API for Quotas service.
|
||||
// All implementations should embed UnimplementedQuotasServer
|
||||
// for forward compatibility
|
||||
type QuotasServer interface {
|
||||
// Get current quota usage and limits
|
||||
GetQuotaUsage(context.Context, *QuotaUsageRequest) (*QuotaUsageResponse, error)
|
||||
}
|
||||
|
||||
// UnimplementedQuotasServer should be embedded to have forward compatible implementations.
|
||||
type UnimplementedQuotasServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedQuotasServer) GetQuotaUsage(context.Context, *QuotaUsageRequest) (*QuotaUsageResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetQuotaUsage not implemented")
|
||||
}
|
||||
|
||||
// UnsafeQuotasServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to QuotasServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeQuotasServer interface {
|
||||
mustEmbedUnimplementedQuotasServer()
|
||||
}
|
||||
|
||||
func RegisterQuotasServer(s grpc.ServiceRegistrar, srv QuotasServer) {
|
||||
s.RegisterService(&Quotas_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _Quotas_GetQuotaUsage_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(QuotaUsageRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(QuotasServer).GetQuotaUsage(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Quotas_GetQuotaUsage_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(QuotasServer).GetQuotaUsage(ctx, req.(*QuotaUsageRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// Quotas_ServiceDesc is the grpc.ServiceDesc for Quotas service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var Quotas_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "resource.Quotas",
|
||||
HandlerType: (*QuotasServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "GetQuotaUsage",
|
||||
Handler: _Quotas_GetQuotaUsage_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "resource.proto",
|
||||
}
|
||||
|
||||
@@ -312,6 +312,7 @@ func (s *service) starting(ctx context.Context) error {
|
||||
resourcepb.RegisterManagedObjectIndexServer(srv, server)
|
||||
resourcepb.RegisterBlobStoreServer(srv, server)
|
||||
resourcepb.RegisterDiagnosticsServer(srv, server)
|
||||
resourcepb.RegisterQuotasServer(srv, server)
|
||||
grpc_health_v1.RegisterHealthServer(srv, healthService)
|
||||
|
||||
// register reflection service
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
package identity
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
iamv0alpha1 "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/apiserver/rest"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/tests/apis"
|
||||
"github.com/grafana/grafana/pkg/tests/testinfra"
|
||||
"github.com/grafana/grafana/pkg/util/testutil"
|
||||
)
|
||||
|
||||
func TestIntegrationTeamSearch(t *testing.T) {
|
||||
testutil.SkipIntegrationTestInShortMode(t)
|
||||
|
||||
// TODO: Add rest.Mode3 and rest.Mode4 when they're supported
|
||||
modes := []rest.DualWriterMode{rest.Mode0, rest.Mode1, rest.Mode2}
|
||||
for _, mode := range modes {
|
||||
t.Run(fmt.Sprintf("Team search with dual writer mode %d", mode), func(t *testing.T) {
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
AppModeProduction: false,
|
||||
DisableAnonymous: true,
|
||||
APIServerStorageType: "unified",
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
"teams.iam.grafana.app": {
|
||||
DualWriterMode: mode,
|
||||
},
|
||||
},
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs,
|
||||
featuremgmt.FlagKubernetesAuthnMutation,
|
||||
},
|
||||
})
|
||||
doTeamSearchTests(t, helper)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func doTeamSearchTests(t *testing.T, helper *apis.K8sTestHelper) {
|
||||
ctx := context.Background()
|
||||
namespace := helper.Namespacer(helper.Org1.Admin.Identity.GetOrgID())
|
||||
|
||||
// Create teams for testing
|
||||
teamClient := helper.GetResourceClient(apis.ResourceClientArgs{
|
||||
User: helper.Org1.Admin,
|
||||
Namespace: namespace,
|
||||
GVR: gvrTeams,
|
||||
})
|
||||
|
||||
team1, err := teamClient.Resource.Create(ctx, helper.LoadYAMLOrJSONFile("testdata/team-test-create-v0.yaml"), metav1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, team1)
|
||||
|
||||
// Create a second team with a different name
|
||||
team2YAML := helper.LoadYAMLOrJSONFile("testdata/team-test-create-v0.yaml")
|
||||
team2YAML.Object["metadata"].(map[string]interface{})["name"] = "testteam2"
|
||||
team2YAML.Object["spec"].(map[string]interface{})["title"] = "Another Team"
|
||||
team2YAML.Object["spec"].(map[string]interface{})["email"] = "anotherteam@example.com"
|
||||
|
||||
team2, err := teamClient.Resource.Create(ctx, team2YAML, metav1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, team2)
|
||||
|
||||
t.Run("should search teams without query parameter", func(t *testing.T) {
|
||||
path := fmt.Sprintf("/apis/iam.grafana.app/v0alpha1/namespaces/%s/searchTeams", namespace)
|
||||
var result iamv0alpha1.TeamSearchResults
|
||||
|
||||
response := apis.DoRequest(helper, apis.RequestParams{
|
||||
User: helper.Org1.Admin,
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
}, &result)
|
||||
|
||||
require.NotNil(t, response)
|
||||
require.Equal(t, http.StatusOK, response.Response.StatusCode)
|
||||
require.NotNil(t, response.Result)
|
||||
require.GreaterOrEqual(t, result.TotalHits, int64(2), "should find at least 2 teams")
|
||||
require.GreaterOrEqual(t, len(result.Hits), 2, "should return at least 2 hits")
|
||||
|
||||
for _, hit := range result.Hits {
|
||||
if hit.Name == team1.GetName() {
|
||||
require.Equal(t, "Test Team 1", hit.Title)
|
||||
require.Equal(t, "testteam1@example123.com", hit.Email)
|
||||
}
|
||||
if hit.Name == team2.GetName() {
|
||||
require.Equal(t, "Another Team", hit.Title)
|
||||
require.Equal(t, "anotherteam@example.com", hit.Email)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("should search teams with query parameter", func(t *testing.T) {
|
||||
path := fmt.Sprintf("/apis/iam.grafana.app/v0alpha1/namespaces/%s/searchTeams?query=another", namespace)
|
||||
var result iamv0alpha1.TeamSearchResults
|
||||
|
||||
response := apis.DoRequest(helper, apis.RequestParams{
|
||||
User: helper.Org1.Admin,
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
}, &result)
|
||||
|
||||
require.NotNil(t, response)
|
||||
require.Equal(t, http.StatusOK, response.Response.StatusCode)
|
||||
require.NotNil(t, response.Result)
|
||||
require.Equal(t, result.TotalHits, int64(1), "should find 1 team matching 'another'")
|
||||
require.Equal(t, len(result.Hits), 1, "should return 1 hit")
|
||||
require.Equal(t, result.Hits[0].Name, team2.GetName())
|
||||
require.Equal(t, result.Hits[0].Title, "Another Team")
|
||||
require.Equal(t, result.Hits[0].Email, "anotherteam@example.com")
|
||||
})
|
||||
|
||||
t.Run("should return no results when query does not match any teams", func(t *testing.T) {
|
||||
path := fmt.Sprintf("/apis/iam.grafana.app/v0alpha1/namespaces/%s/searchTeams?query=nonexistent", namespace)
|
||||
var result iamv0alpha1.TeamSearchResults
|
||||
|
||||
response := apis.DoRequest(helper, apis.RequestParams{
|
||||
User: helper.Org1.Admin,
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
}, &result)
|
||||
|
||||
require.NotNil(t, response)
|
||||
require.Equal(t, http.StatusOK, response.Response.StatusCode)
|
||||
require.NotNil(t, response.Result)
|
||||
require.Equal(t, int64(0), result.TotalHits, "should return 0 hits when query does not match any teams")
|
||||
require.Equal(t, 0, len(result.Hits), "should return 0 hits when query does not match any teams")
|
||||
})
|
||||
|
||||
t.Run("should search teams with limit parameter", func(t *testing.T) {
|
||||
path := fmt.Sprintf("/apis/iam.grafana.app/v0alpha1/namespaces/%s/searchTeams?limit=1", namespace)
|
||||
var result iamv0alpha1.TeamSearchResults
|
||||
|
||||
response := apis.DoRequest(helper, apis.RequestParams{
|
||||
User: helper.Org1.Admin,
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
}, &result)
|
||||
|
||||
require.NotNil(t, response)
|
||||
require.Equal(t, http.StatusOK, response.Response.StatusCode)
|
||||
require.NotNil(t, response.Result)
|
||||
require.Equal(t, 1, len(result.Hits), "should return 1 hit when limit is 1")
|
||||
})
|
||||
|
||||
t.Run("should search teams with pagination", func(t *testing.T) {
|
||||
// First page
|
||||
path := fmt.Sprintf("/apis/iam.grafana.app/v0alpha1/namespaces/%s/searchTeams?limit=1&page=1", namespace)
|
||||
var result1 iamv0alpha1.TeamSearchResults
|
||||
|
||||
response1 := apis.DoRequest(helper, apis.RequestParams{
|
||||
User: helper.Org1.Admin,
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
}, &result1)
|
||||
|
||||
require.NotNil(t, response1)
|
||||
require.Equal(t, http.StatusOK, response1.Response.StatusCode)
|
||||
require.NotNil(t, response1.Result)
|
||||
require.Equal(t, int64(0), result1.Offset, "first page should have offset 0")
|
||||
|
||||
// Second page
|
||||
path2 := fmt.Sprintf("/apis/iam.grafana.app/v0alpha1/namespaces/%s/searchTeams?limit=1&page=2", namespace)
|
||||
var result2 iamv0alpha1.TeamSearchResults
|
||||
|
||||
response2 := apis.DoRequest(helper, apis.RequestParams{
|
||||
User: helper.Org1.Admin,
|
||||
Method: http.MethodGet,
|
||||
Path: path2,
|
||||
}, &result2)
|
||||
|
||||
require.NotNil(t, response2)
|
||||
require.Equal(t, http.StatusOK, response2.Response.StatusCode)
|
||||
require.NotNil(t, response2.Result)
|
||||
require.Equal(t, int64(1), result2.Offset, "second page should have offset 1")
|
||||
})
|
||||
|
||||
t.Run("should search teams with offset parameter", func(t *testing.T) {
|
||||
path := fmt.Sprintf("/apis/iam.grafana.app/v0alpha1/namespaces/%s/searchTeams?offset=1&limit=1", namespace)
|
||||
var result iamv0alpha1.TeamSearchResults
|
||||
|
||||
response := apis.DoRequest(helper, apis.RequestParams{
|
||||
User: helper.Org1.Admin,
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
}, &result)
|
||||
|
||||
require.NotNil(t, response)
|
||||
require.Equal(t, http.StatusOK, response.Response.StatusCode)
|
||||
require.NotNil(t, response.Result)
|
||||
require.GreaterOrEqual(t, result.TotalHits, int64(2), "should find at least 2 teams")
|
||||
require.Equal(t, 1, len(result.Hits), "should return 1 hit")
|
||||
require.Equal(t, int64(1), result.Offset, "should return offset 1")
|
||||
})
|
||||
}
|
||||
@@ -3020,6 +3020,9 @@
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"regexApplyTo": {
|
||||
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableRegexApplyTo"
|
||||
},
|
||||
"skipUrlSync": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
@@ -3930,6 +3933,14 @@
|
||||
"onTimeRangeChanged"
|
||||
]
|
||||
},
|
||||
"com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableRegexApplyTo": {
|
||||
"description": "Determine whether regex applies to variable value or display text\nAccepted values are `value` (apply to value used in queries) or `text` (apply to display text shown to users)",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"value",
|
||||
"text"
|
||||
]
|
||||
},
|
||||
"com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableSort": {
|
||||
"description": "Sort variable options\nAccepted values are:\n`disabled`: No sorting\n`alphabeticalAsc`: Alphabetical ASC\n`alphabeticalDesc`: Alphabetical DESC\n`numericalAsc`: Numerical ASC\n`numericalDesc`: Numerical DESC\n`alphabeticalCaseInsensitiveAsc`: Alphabetical Case Insensitive ASC\n`alphabeticalCaseInsensitiveDesc`: Alphabetical Case Insensitive DESC\n`naturalAsc`: Natural ASC\n`naturalDesc`: Natural DESC\nVariableSort enum with default value",
|
||||
"type": "string",
|
||||
|
||||
@@ -3047,6 +3047,9 @@
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"regexApplyTo": {
|
||||
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableRegexApplyTo"
|
||||
},
|
||||
"skipUrlSync": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
@@ -3957,6 +3960,14 @@
|
||||
"onTimeRangeChanged"
|
||||
]
|
||||
},
|
||||
"com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableRegexApplyTo": {
|
||||
"description": "Determine whether regex applies to variable value or display text\nAccepted values are `value` (apply to value used in queries) or `text` (apply to display text shown to users)",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"value",
|
||||
"text"
|
||||
]
|
||||
},
|
||||
"com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableSort": {
|
||||
"description": "Sort variable options\nAccepted values are:\n`disabled`: No sorting\n`alphabeticalAsc`: Alphabetical ASC\n`alphabeticalDesc`: Alphabetical DESC\n`numericalAsc`: Numerical ASC\n`numericalDesc`: Numerical DESC\n`alphabeticalCaseInsensitiveAsc`: Alphabetical Case Insensitive ASC\n`alphabeticalCaseInsensitiveDesc`: Alphabetical Case Insensitive DESC\n`naturalAsc`: Natural ASC\n`naturalDesc`: Natural DESC\nVariableSort enum with default value",
|
||||
"type": "string",
|
||||
|
||||
+3
@@ -323,6 +323,7 @@ exports[`Given a scene with custom quick ranges should save quick ranges to save
|
||||
},
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"regexApplyTo": "value",
|
||||
"type": "query",
|
||||
},
|
||||
{
|
||||
@@ -1049,6 +1050,7 @@ exports[`transformSceneToSaveModel Given a simple scene with custom settings Sho
|
||||
},
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"regexApplyTo": "value",
|
||||
"type": "query",
|
||||
},
|
||||
{
|
||||
@@ -1408,6 +1410,7 @@ exports[`transformSceneToSaveModel Given a simple scene with variables Should tr
|
||||
},
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"regexApplyTo": "value",
|
||||
"type": "query",
|
||||
},
|
||||
{
|
||||
|
||||
+1
@@ -173,6 +173,7 @@ exports[`transformSceneToSaveModelSchemaV2 should transform scene to save model
|
||||
},
|
||||
"refresh": "onDashboardLoad",
|
||||
"regex": "regex1",
|
||||
"regexApplyTo": "value",
|
||||
"skipUrlSync": false,
|
||||
"sort": "alphabeticalDesc",
|
||||
},
|
||||
|
||||
@@ -141,6 +141,7 @@ describe('sceneVariablesSetToVariables', () => {
|
||||
"query": "query",
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"regexApplyTo": "value",
|
||||
"staticOptions": [
|
||||
{
|
||||
"text": "test",
|
||||
@@ -205,6 +206,7 @@ describe('sceneVariablesSetToVariables', () => {
|
||||
"query": "query",
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"regexApplyTo": "value",
|
||||
"staticOptions": [
|
||||
{
|
||||
"text": "test",
|
||||
@@ -1084,6 +1086,7 @@ describe('sceneVariablesSetToVariables', () => {
|
||||
},
|
||||
"refresh": "onDashboardLoad",
|
||||
"regex": "",
|
||||
"regexApplyTo": "value",
|
||||
"skipUrlSync": false,
|
||||
"sort": "disabled",
|
||||
"staticOptions": [
|
||||
|
||||
@@ -84,6 +84,7 @@ export function sceneVariablesSetToVariables(set: SceneVariables, keepQueryOptio
|
||||
sort: variable.state.sort,
|
||||
refresh: variable.state.refresh,
|
||||
regex: variable.state.regex,
|
||||
regexApplyTo: variable.state.regexApplyTo,
|
||||
allValue: variable.state.allValue,
|
||||
includeAll: variable.state.includeAll,
|
||||
multi: variable.state.isMulti,
|
||||
@@ -375,6 +376,7 @@ export function sceneVariablesSetToSchemaV2Variables(
|
||||
sort: transformSortVariableToEnum(variable.state.sort),
|
||||
refresh: transformVariableRefreshToEnum(variable.state.refresh),
|
||||
regex: variable.state.regex ?? '',
|
||||
regexApplyTo: variable.state.regexApplyTo ?? 'value',
|
||||
allValue: variable.state.allValue,
|
||||
includeAll: variable.state.includeAll || false,
|
||||
multi: variable.state.isMulti || false,
|
||||
|
||||
@@ -366,6 +366,7 @@ function createSceneVariableFromVariableModel(variable: TypedVariableModelV2): S
|
||||
sort: transformSortVariableToEnumV1(variable.spec.sort),
|
||||
refresh: transformVariableRefreshToEnumV1(variable.spec.refresh),
|
||||
regex: variable.spec.regex,
|
||||
regexApplyTo: variable.spec.regexApplyTo,
|
||||
allValue: variable.spec.allValue || undefined,
|
||||
includeAll: variable.spec.includeAll,
|
||||
defaultToAll: Boolean(variable.spec.includeAll),
|
||||
|
||||
+1
@@ -283,6 +283,7 @@ describe('transformSceneToSaveModelSchemaV2', () => {
|
||||
sort: VariableSortV1.alphabeticalDesc,
|
||||
refresh: VariableRefresh.onDashboardLoad,
|
||||
regex: 'regex1',
|
||||
regexApplyTo: 'value',
|
||||
allValue: '*',
|
||||
includeAll: true,
|
||||
isMulti: true,
|
||||
|
||||
+23
@@ -71,6 +71,7 @@ describe('QueryVariableEditorForm', () => {
|
||||
const mockOnQueryChange = jest.fn();
|
||||
const mockOnLegacyQueryChange = jest.fn();
|
||||
const mockOnRegExChange = jest.fn();
|
||||
const mockOnRegexApplyToChange = jest.fn();
|
||||
const mockOnSortChange = jest.fn();
|
||||
const mockOnRefreshChange = jest.fn();
|
||||
const mockOnMultiChange = jest.fn();
|
||||
@@ -89,6 +90,8 @@ describe('QueryVariableEditorForm', () => {
|
||||
timeRange: getDefaultTimeRange(),
|
||||
regex: '.*',
|
||||
onRegExChange: mockOnRegExChange,
|
||||
regexApplyTo: 'value',
|
||||
onRegexApplyToChange: mockOnRegexApplyToChange,
|
||||
sort: VariableSort.alphabeticalAsc,
|
||||
onSortChange: mockOnSortChange,
|
||||
refresh: VariableRefresh.onDashboardLoad,
|
||||
@@ -126,6 +129,9 @@ describe('QueryVariableEditorForm', () => {
|
||||
const regexInput = getByTestId(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsRegExInputV2
|
||||
);
|
||||
const regexApplyToSelect = getByTestId(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsRegExApplyToSelectV2
|
||||
);
|
||||
const sortSelect = getByTestId(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsSortSelectV2
|
||||
);
|
||||
@@ -154,6 +160,8 @@ describe('QueryVariableEditorForm', () => {
|
||||
expect(dataSourcePicker.getAttribute('placeholder')).toBe('Default Test Data Source');
|
||||
expect(regexInput).toBeInTheDocument();
|
||||
expect(regexInput).toHaveValue('.*');
|
||||
expect(regexApplyToSelect).toBeInTheDocument();
|
||||
expect(getByRole('radio', { name: 'Variable value' })).toBeChecked();
|
||||
expect(sortSelect).toBeInTheDocument();
|
||||
expect(sortSelect).toHaveTextContent('Alphabetical (asc)');
|
||||
expect(refreshSelect).toBeInTheDocument();
|
||||
@@ -213,6 +221,21 @@ describe('QueryVariableEditorForm', () => {
|
||||
).toBe('.?');
|
||||
});
|
||||
|
||||
it('should call onRegexApplyToChange when selecting the regex apply to option', async () => {
|
||||
const {
|
||||
renderer: { getByTestId },
|
||||
} = await setup();
|
||||
const regexApplyToSelect = getByTestId(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsRegExApplyToSelectV2
|
||||
);
|
||||
await userEvent.click(regexApplyToSelect);
|
||||
const anotherOption = screen.getByText('Display text');
|
||||
await userEvent.click(anotherOption);
|
||||
|
||||
expect(mockOnRegexApplyToChange).toHaveBeenCalledTimes(1);
|
||||
expect(mockOnRegexApplyToChange).toHaveBeenCalledWith('text');
|
||||
});
|
||||
|
||||
it('should call onSortChange when changing the sort', async () => {
|
||||
const {
|
||||
renderer: { getByTestId },
|
||||
|
||||
+12
-29
@@ -1,14 +1,15 @@
|
||||
import { FormEvent, useCallback } from 'react';
|
||||
import { useAsync } from 'react-use';
|
||||
|
||||
import { DataSourceInstanceSettings, SelectableValue, TimeRange } from '@grafana/data';
|
||||
import { DataSourceInstanceSettings, SelectableValue, TimeRange, VariableRegexApplyTo } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { getDataSourceSrv } from '@grafana/runtime';
|
||||
import { QueryVariable } from '@grafana/scenes';
|
||||
import { DataSourceRef, VariableRefresh, VariableSort } from '@grafana/schema';
|
||||
import { Field, TextLink } from '@grafana/ui';
|
||||
import { Field } from '@grafana/ui';
|
||||
import { QueryEditor } from 'app/features/dashboard-scene/settings/variables/components/QueryEditor';
|
||||
import { QueryVariableRegexForm } from 'app/features/dashboard-scene/settings/variables/components/QueryVariableRegexForm';
|
||||
import { SelectionOptionsForm } from 'app/features/dashboard-scene/settings/variables/components/SelectionOptionsForm';
|
||||
import { DataSourcePicker } from 'app/features/datasources/components/picker/DataSourcePicker';
|
||||
import { getVariableQueryEditor } from 'app/features/variables/editor/getVariableQueryEditor';
|
||||
@@ -21,7 +22,6 @@ import {
|
||||
} from 'app/features/variables/query/QueryVariableStaticOptions';
|
||||
|
||||
import { VariableLegend } from './VariableLegend';
|
||||
import { VariableTextAreaField } from './VariableTextAreaField';
|
||||
|
||||
type VariableQueryType = QueryVariable['state']['query'];
|
||||
|
||||
@@ -34,6 +34,8 @@ interface QueryVariableEditorFormProps {
|
||||
timeRange: TimeRange;
|
||||
regex: string | null;
|
||||
onRegExChange: (event: FormEvent<HTMLTextAreaElement>) => void;
|
||||
regexApplyTo?: VariableRegexApplyTo;
|
||||
onRegexApplyToChange?: (event: VariableRegexApplyTo) => void;
|
||||
sort: VariableSort;
|
||||
onSortChange: (option: SelectableValue<VariableSort>) => void;
|
||||
refresh: VariableRefresh;
|
||||
@@ -61,6 +63,8 @@ export function QueryVariableEditorForm({
|
||||
timeRange,
|
||||
regex,
|
||||
onRegExChange,
|
||||
regexApplyTo,
|
||||
onRegexApplyToChange,
|
||||
sort,
|
||||
onSortChange,
|
||||
refresh,
|
||||
@@ -131,32 +135,11 @@ export function QueryVariableEditorForm({
|
||||
/>
|
||||
)}
|
||||
|
||||
<VariableTextAreaField
|
||||
defaultValue={regex ?? ''}
|
||||
name={t('dashboard-scene.query-variable-editor-form.name-regex', 'Regex')}
|
||||
description={
|
||||
<div>
|
||||
<Trans i18nKey="dashboard-scene.query-variable-editor-form.description-optional">
|
||||
Optional, if you want to extract part of a series name or metric node segment.
|
||||
</Trans>
|
||||
<br />
|
||||
<Trans i18nKey="dashboard-scene.query-variable-editor-form.description-examples">
|
||||
Named capture groups can be used to separate the display text and value (
|
||||
<TextLink
|
||||
href="https://grafana.com/docs/grafana/latest/variables/filter-variables-with-regex#filter-and-modify-using-named-text-and-value-capture-groups"
|
||||
external
|
||||
>
|
||||
see examples
|
||||
</TextLink>
|
||||
).
|
||||
</Trans>
|
||||
</div>
|
||||
}
|
||||
// eslint-disable-next-line @grafana/i18n/no-untranslated-strings
|
||||
placeholder="/.*-(?<text>.*)-(?<value>.*)-.*/"
|
||||
onBlur={onRegExChange}
|
||||
testId={selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsRegExInputV2}
|
||||
width={52}
|
||||
<QueryVariableRegexForm
|
||||
regex={regex}
|
||||
regexApplyTo={regexApplyTo}
|
||||
onRegExChange={onRegExChange}
|
||||
onRegexApplyToChange={onRegexApplyToChange}
|
||||
/>
|
||||
|
||||
<QueryVariableSortSelect
|
||||
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
import { render, fireEvent } from '@testing-library/react';
|
||||
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
|
||||
import { QueryVariableRegexForm } from './QueryVariableRegexForm';
|
||||
|
||||
describe('QueryVariableRegexForm', () => {
|
||||
const onRegExChange = jest.fn();
|
||||
const onRegexApplyToChange = jest.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should render the form fields correctly', () => {
|
||||
const { getByTestId, getByRole } = render(
|
||||
<QueryVariableRegexForm
|
||||
regex=".*test.*"
|
||||
regexApplyTo="value"
|
||||
onRegExChange={onRegExChange}
|
||||
onRegexApplyToChange={onRegexApplyToChange}
|
||||
/>
|
||||
);
|
||||
|
||||
const regexInput = getByTestId(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsRegExInputV2
|
||||
);
|
||||
const regexApplyToField = getByTestId(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsRegExApplyToSelectV2
|
||||
);
|
||||
|
||||
expect(regexInput).toBeInTheDocument();
|
||||
expect(regexInput).toHaveValue('.*test.*');
|
||||
expect(regexApplyToField).toBeInTheDocument();
|
||||
expect(getByRole('radio', { name: 'Variable value' })).toBeChecked();
|
||||
expect(getByRole('radio', { name: 'Display text' })).not.toBeChecked();
|
||||
});
|
||||
|
||||
it('should render with "Display text" option selected', () => {
|
||||
const { getByRole } = render(
|
||||
<QueryVariableRegexForm
|
||||
regex=""
|
||||
regexApplyTo="text"
|
||||
onRegExChange={onRegExChange}
|
||||
onRegexApplyToChange={onRegexApplyToChange}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(getByRole('radio', { name: 'Display text' })).toBeChecked();
|
||||
expect(getByRole('radio', { name: 'Variable value' })).not.toBeChecked();
|
||||
});
|
||||
|
||||
it('should default to "Variable value" when regexApplyTo is not provided', () => {
|
||||
const { getByRole } = render(
|
||||
<QueryVariableRegexForm regex="" onRegExChange={onRegExChange} onRegexApplyToChange={onRegexApplyToChange} />
|
||||
);
|
||||
|
||||
expect(getByRole('radio', { name: 'Variable value' })).toBeChecked();
|
||||
});
|
||||
|
||||
it('should call onRegExChange when regex input is blurred', () => {
|
||||
const { getByTestId } = render(
|
||||
<QueryVariableRegexForm
|
||||
regex=".*"
|
||||
regexApplyTo="value"
|
||||
onRegExChange={onRegExChange}
|
||||
onRegexApplyToChange={onRegexApplyToChange}
|
||||
/>
|
||||
);
|
||||
|
||||
const regexInput = getByTestId(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsRegExInputV2
|
||||
);
|
||||
|
||||
fireEvent.blur(regexInput);
|
||||
|
||||
expect(onRegExChange).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should call onRegexApplyToChange when radio option is changed', () => {
|
||||
const { getByRole } = render(
|
||||
<QueryVariableRegexForm
|
||||
regex=""
|
||||
regexApplyTo="value"
|
||||
onRegExChange={onRegExChange}
|
||||
onRegexApplyToChange={onRegexApplyToChange}
|
||||
/>
|
||||
);
|
||||
|
||||
const displayTextOption = getByRole('radio', { name: 'Display text' });
|
||||
fireEvent.click(displayTextOption);
|
||||
|
||||
expect(onRegexApplyToChange).toHaveBeenCalledTimes(1);
|
||||
expect(onRegexApplyToChange).toHaveBeenCalledWith('text');
|
||||
});
|
||||
});
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
import { useMemo, FormEvent } from 'react';
|
||||
|
||||
import { VariableRegexApplyTo, SelectableValue } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { Field, Stack, TextLink, RadioButtonGroup, Box } from '@grafana/ui';
|
||||
|
||||
import { VariableTextAreaField } from '../components/VariableTextAreaField';
|
||||
|
||||
interface Props {
|
||||
regex: string | null;
|
||||
onRegExChange: (event: FormEvent<HTMLTextAreaElement>) => void;
|
||||
regexApplyTo?: VariableRegexApplyTo;
|
||||
onRegexApplyToChange?: (option: VariableRegexApplyTo) => void;
|
||||
}
|
||||
|
||||
export function QueryVariableRegexForm({ regex, regexApplyTo, onRegExChange, onRegexApplyToChange }: Props) {
|
||||
const APPLY_REGEX_TO_OPTIONS: Array<SelectableValue<VariableRegexApplyTo>> = useMemo(
|
||||
() => [
|
||||
{
|
||||
label: t('dashboard-scene.query-variable-editor-form.regex-apply-to-options.label.value', 'Variable value'),
|
||||
value: 'value',
|
||||
},
|
||||
{
|
||||
label: t('dashboard-scene.query-variable-editor-form.regex-apply-to-options.label.text', 'Display text'),
|
||||
value: 'text',
|
||||
},
|
||||
],
|
||||
[]
|
||||
);
|
||||
|
||||
const regexApplyToValue = useMemo(
|
||||
() => APPLY_REGEX_TO_OPTIONS.find((o) => o.value === regexApplyTo)?.value ?? APPLY_REGEX_TO_OPTIONS[0].value,
|
||||
[regexApplyTo, APPLY_REGEX_TO_OPTIONS]
|
||||
);
|
||||
|
||||
return (
|
||||
<Box marginBottom={2}>
|
||||
<Stack direction="column" gap={2}>
|
||||
<VariableTextAreaField
|
||||
defaultValue={regex ?? ''}
|
||||
name={t('dashboard-scene.query-variable-editor-form.name-regex', 'Regex')}
|
||||
description={
|
||||
<div>
|
||||
<Trans i18nKey="dashboard-scene.query-variable-editor-form.description-optional">
|
||||
Optional, if you want to extract part of a series name or metric node segment.
|
||||
</Trans>
|
||||
<br />
|
||||
<Trans i18nKey="dashboard-scene.query-variable-editor-form.description-examples">
|
||||
Named capture groups can be used to separate the display text and value (
|
||||
<TextLink
|
||||
href="https://grafana.com/docs/grafana/latest/variables/filter-variables-with-regex#filter-and-modify-using-named-text-and-value-capture-groups"
|
||||
external
|
||||
>
|
||||
see examples
|
||||
</TextLink>
|
||||
).
|
||||
</Trans>
|
||||
</div>
|
||||
}
|
||||
// eslint-disable-next-line @grafana/i18n/no-untranslated-strings
|
||||
placeholder="/.*-(?<text>.*)-(?<value>.*)-.*/"
|
||||
onBlur={onRegExChange}
|
||||
testId={selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsRegExInputV2}
|
||||
width={52}
|
||||
noMargin
|
||||
/>
|
||||
|
||||
{onRegexApplyToChange && (
|
||||
<Field
|
||||
label={t('dashboard-scene.query-variable-editor-form.label-regex-apply-to', 'Apply regex to')}
|
||||
description={t(
|
||||
'dashboard-scene.query-variable-editor-form.description-regex-apply-to',
|
||||
'Choose whether to apply the regex pattern to the variable value or display text'
|
||||
)}
|
||||
data-testid={
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsRegExApplyToSelectV2
|
||||
}
|
||||
noMargin
|
||||
>
|
||||
<RadioButtonGroup
|
||||
options={APPLY_REGEX_TO_OPTIONS}
|
||||
onChange={onRegexApplyToChange}
|
||||
value={regexApplyToValue}
|
||||
/>
|
||||
</Field>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
+5
-4
@@ -1,7 +1,6 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { useId } from '@react-aria/utils';
|
||||
import { FormEvent, PropsWithChildren, ReactElement } from 'react';
|
||||
import * as React from 'react';
|
||||
import { FormEvent, PropsWithChildren, ReactElement, ReactNode } from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Field, TextArea, useStyles2 } from '@grafana/ui';
|
||||
@@ -17,7 +16,8 @@ interface VariableTextAreaFieldProps {
|
||||
required?: boolean;
|
||||
testId?: string;
|
||||
onBlur?: (event: FormEvent<HTMLTextAreaElement>) => void;
|
||||
description?: React.ReactNode;
|
||||
description?: ReactNode;
|
||||
noMargin?: boolean;
|
||||
}
|
||||
|
||||
export function VariableTextAreaField({
|
||||
@@ -31,13 +31,14 @@ export function VariableTextAreaField({
|
||||
ariaLabel,
|
||||
required,
|
||||
width,
|
||||
noMargin,
|
||||
testId,
|
||||
}: PropsWithChildren<VariableTextAreaFieldProps>): ReactElement {
|
||||
const styles = useStyles2(getStyles);
|
||||
const id = useId();
|
||||
|
||||
return (
|
||||
<Field label={name} description={description} htmlFor={id}>
|
||||
<Field label={name} description={description} htmlFor={id} noMargin={noMargin}>
|
||||
<TextArea
|
||||
id={id}
|
||||
rows={2}
|
||||
|
||||
+1
@@ -73,6 +73,7 @@ describe('QueryVariableEditor', () => {
|
||||
},
|
||||
query: 'my-query',
|
||||
regex: '.*',
|
||||
regexApplyTo: 'value',
|
||||
sort: VariableSort.alphabeticalAsc,
|
||||
refresh: VariableRefresh.onDashboardLoad,
|
||||
isMulti: true,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user