Compare commits

..

17 Commits

Author SHA1 Message Date
Ryan McKinley d5df03e531 update snapshot 2026-01-14 08:42:00 +03:00
Ryan McKinley 2308b5458e Merge remote-tracking branch 'origin/main' into dual-write-inline-secure-values 2026-01-14 08:40:43 +03:00
Ryan McKinley 1a2c3bdbc9 update snapshot 2026-01-13 16:49:23 +03:00
Ryan McKinley 8af89b1210 ensure GVK is configured 2026-01-13 16:34:59 +03:00
Ryan McKinley 4b24e63e0b Merge remote-tracking branch 'origin/main' into dual-write-inline-secure-values 2026-01-13 16:18:04 +03:00
Ryan McKinley 68e7d66e54 merge main 2026-01-13 16:11:24 +03:00
Ryan McKinley ef0601b85e add delete to integration tests 2026-01-12 18:22:50 +03:00
Ryan McKinley e25d09ff3e support generic response 2026-01-12 18:08:56 +03:00
Ryan McKinley b7269073b2 lint fix 2026-01-12 15:30:07 +03:00
Ryan McKinley 29dfdafad5 Merge remote-tracking branch 'origin/main' into dual-write-inline-secure-values 2026-01-12 15:03:00 +03:00
Ryan McKinley b7da61c260 testing CRUD 2026-01-12 14:41:31 +03:00
Ryan McKinley 0e6ad2e7c8 cleanup 2026-01-12 14:22:07 +03:00
Ryan McKinley aae69c1e75 revert signature change 2026-01-12 11:05:39 +03:00
Ryan McKinley 89dd3870b3 with context 2026-01-12 10:47:05 +03:00
Ryan McKinley 3b577e2c42 with context 2026-01-12 10:46:52 +03:00
Ryan McKinley 7ff004f775 dual write secure values for create 2026-01-12 09:37:39 +03:00
Ryan McKinley 4d26d0cd5c dual write secure values 2026-01-12 09:27:36 +03:00
183 changed files with 1819 additions and 5070 deletions
-1
View File
@@ -543,7 +543,6 @@ i18next.config.ts @grafana/grafana-frontend-platform
/packages/grafana-data/tsconfig.json @grafana/grafana-frontend-platform
/packages/grafana-data/test/ @grafana/grafana-frontend-platform
/packages/grafana-data/typings/ @grafana/grafana-frontend-platform
/packages/grafana-data/scripts/ @grafana/grafana-frontend-platform
/packages/grafana-data/src/**/*logs* @grafana/observability-logs
/packages/grafana-data/src/context/plugins/ @grafana/plugins-platform-frontend
-2
View File
@@ -121,8 +121,6 @@ linters:
- '**/pkg/tsdb/zipkin/**/*'
- '**/pkg/tsdb/jaeger/*'
- '**/pkg/tsdb/jaeger/**/*'
- '**/pkg/tsdb/elasticsearch/*'
- '**/pkg/tsdb/elasticsearch/**/*'
deny:
- pkg: github.com/grafana/grafana/pkg/api
desc: Core plugins are not allowed to depend on Grafana core packages
@@ -28,7 +28,7 @@ type check struct {
PluginStore pluginstore.Store
PluginContextProvider PluginContextProvider
PluginClient plugins.Client
PluginRepo checks.PluginInfoGetter
PluginRepo repo.Service
GrafanaVersion string
pluginCanBeInstalledCache map[string]bool
pluginExistsCacheMu sync.RWMutex
@@ -39,7 +39,7 @@ func New(
pluginStore pluginstore.Store,
pluginContextProvider PluginContextProvider,
pluginClient plugins.Client,
pluginRepo checks.PluginInfoGetter,
pluginRepo repo.Service,
grafanaVersion string,
) checks.Check {
return &check{
@@ -15,7 +15,7 @@ import (
type missingPluginStep struct {
PluginStore pluginstore.Store
PluginRepo checks.PluginInfoGetter
PluginRepo repo.Service
GrafanaVersion string
}
-8
View File
@@ -5,7 +5,6 @@ import (
"github.com/grafana/grafana-app-sdk/logging"
advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
"github.com/grafana/grafana/pkg/plugins/repo"
)
// Check returns metadata about the check being executed and the list of Steps
@@ -38,10 +37,3 @@ type Step interface {
// Run executes the step for an item and returns a report
Run(ctx context.Context, log logging.Logger, obj *advisorv0alpha1.CheckSpec, item any) ([]advisorv0alpha1.CheckReportFailure, error)
}
// PluginInfoGetter is a minimal interface for retrieving plugin information from a repository.
// It contains only the GetPluginsInfo method used by plugincheck and datasourcecheck.
type PluginInfoGetter interface {
// GetPluginsInfo will return a list of plugins from grafana.com/api/plugins.
GetPluginsInfo(ctx context.Context, options repo.GetPluginsInfoOptions, compatOpts repo.CompatOpts) ([]repo.PluginInfo, error)
}
@@ -17,7 +17,7 @@ const (
func New(
pluginStore pluginstore.Store,
pluginRepo checks.PluginInfoGetter,
pluginRepo repo.Service,
updateChecker pluginchecker.PluginUpdateChecker,
pluginErrorResolver plugins.ErrorResolver,
grafanaVersion string,
@@ -33,7 +33,7 @@ func New(
type check struct {
PluginStore pluginstore.Store
PluginRepo checks.PluginInfoGetter
PluginRepo repo.Service
updateChecker pluginchecker.PluginUpdateChecker
pluginErrorResolver plugins.ErrorResolver
GrafanaVersion string
@@ -103,11 +103,10 @@ To configure basic settings for the data source, complete the following steps:
1. Set the data source's basic configuration options:
| Name | Description |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Name** | Sets the name you use to refer to the data source in panels and queries. |
| **Default** | Sets whether the data source is pre-selected for new panels. |
| **Universe Domain** | The universe domain to connect to. For more information, refer to [Documentation on universe domains](https://docs.cloud.google.com/python/docs/reference/monitoring/latest/google.cloud.monitoring_v3.services.service_monitoring_service.ServiceMonitoringServiceAsyncClient#google_cloud_monitoring_v3_services_service_monitoring_service_ServiceMonitoringServiceAsyncClient_universe_domain). Defaults to `googleapis.com`. |
| Name | Description |
| ----------- | ------------------------------------------------------------------------ |
| **Name** | Sets the name you use to refer to the data source in panels and queries. |
| **Default** | Sets whether the data source is pre-selected for new panels. |
### Provision the data source
@@ -130,7 +129,6 @@ datasources:
clientEmail: stackdriver@myproject.iam.gserviceaccount.com
authenticationType: jwt
defaultProject: my-project-name
universeDomain: googleapis.com
secureJsonData:
privateKey: |
-----BEGIN PRIVATE KEY-----
@@ -154,7 +152,6 @@ datasources:
clientEmail: stackdriver@myproject.iam.gserviceaccount.com
authenticationType: jwt
defaultProject: my-project-name
universeDomain: googleapis.com
privateKeyPath: /etc/secrets/gce.pem
```
@@ -169,7 +166,6 @@ datasources:
access: proxy
jsonData:
authenticationType: gce
universeDomain: googleapis.com
```
## Import pre-configured dashboards
@@ -87,7 +87,6 @@ With a Grafana Enterprise license, you also get access to premium data sources,
- [CockroachDB](/grafana/plugins/grafana-cockroachdb-datasource)
- [Databricks](/grafana/plugins/grafana-databricks-datasource)
- [DataDog](/grafana/plugins/grafana-datadog-datasource)
- [IBM Db2](/grafana/plugins/grafana-ibmdb2-datasource)
- [Drone](/grafana/plugins/grafana-drone-datasource)
- [DynamoDB](/grafana/plugins/grafana-dynamodb-datasource/)
- [Dynatrace](/grafana/plugins/grafana-dynatrace-datasource)
@@ -83,7 +83,6 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general-
| `reportingRetries` | Enables rendering retries for the reporting feature |
| `externalServiceAccounts` | Automatic service account and token setup for plugins |
| `cloudWatchBatchQueries` | Runs CloudWatch metrics queries as separate batches |
| `dashboardNewLayouts` | Enables new dashboard layouts |
| `pdfTables` | Enables generating table data as PDF in reporting |
| `canvasPanelPanZoom` | Allow pan and zoom in canvas panel |
| `alertingSaveStateCompressed` | Enables the compressed protobuf-based alert state storage. Default is enabled. |
@@ -30,9 +30,7 @@ refs:
# Datagrid
{{< admonition type="caution" >}}
Starting with Grafana 12.4, Datagrid is deprecated. It will be removed in version 13.0.
{{< /admonition >}}
{{< docs/experimental product="The datagrid visualization" featureFlag="`enableDatagridEditing`" >}}
Datagrids offer you the ability to create, edit, and fine-tune data within Grafana. As such, this panel can act as a data source for other panels
inside a dashboard.
+25
View File
@@ -3743,21 +3743,46 @@
"count": 1
}
},
"public/app/plugins/datasource/elasticsearch/components/QueryEditor/BucketAggregationsEditor/SettingsEditor/DateHistogramSettingsEditor.tsx": {
"@typescript-eslint/consistent-type-assertions": {
"count": 1
}
},
"public/app/plugins/datasource/elasticsearch/components/QueryEditor/BucketAggregationsEditor/SettingsEditor/TermsSettingsEditor.tsx": {
"@typescript-eslint/consistent-type-assertions": {
"count": 1
}
},
"public/app/plugins/datasource/elasticsearch/components/QueryEditor/BucketAggregationsEditor/aggregations.ts": {
"@typescript-eslint/consistent-type-assertions": {
"count": 1
}
},
"public/app/plugins/datasource/elasticsearch/components/QueryEditor/BucketAggregationsEditor/state/reducer.ts": {
"@typescript-eslint/consistent-type-assertions": {
"count": 1
}
},
"public/app/plugins/datasource/elasticsearch/components/QueryEditor/MetricAggregationsEditor/MetricEditor.tsx": {
"@typescript-eslint/consistent-type-assertions": {
"count": 1
}
},
"public/app/plugins/datasource/elasticsearch/components/QueryEditor/MetricAggregationsEditor/SettingsEditor/SettingField.tsx": {
"@typescript-eslint/consistent-type-assertions": {
"count": 2
}
},
"public/app/plugins/datasource/elasticsearch/components/QueryEditor/MetricAggregationsEditor/aggregations.ts": {
"@typescript-eslint/consistent-type-assertions": {
"count": 1
}
},
"public/app/plugins/datasource/elasticsearch/components/QueryEditor/MetricAggregationsEditor/state/reducer.ts": {
"@typescript-eslint/consistent-type-assertions": {
"count": 1
}
},
"public/app/plugins/datasource/elasticsearch/configuration/DataLinks.tsx": {
"no-restricted-syntax": {
"count": 1
+12 -11
View File
@@ -44,8 +44,8 @@ require (
github.com/beevik/etree v1.4.1 // @grafana/grafana-backend-group
github.com/benbjohnson/clock v1.3.5 // @grafana/alerting-backend
github.com/blang/semver/v4 v4.0.0 // indirect; @grafana/grafana-developer-enablement-squad
github.com/blevesearch/bleve/v2 v2.5.7 // @grafana/grafana-search-and-storage
github.com/blevesearch/bleve_index_api v1.3.0 // @grafana/grafana-search-and-storage
github.com/blevesearch/bleve/v2 v2.5.0 // @grafana/grafana-search-and-storage
github.com/blevesearch/bleve_index_api v1.2.7 // @grafana/grafana-search-and-storage
github.com/blugelabs/bluge v0.2.2 // @grafana/grafana-backend-group
github.com/blugelabs/bluge_segment_api v0.2.0 // @grafana/grafana-backend-group
github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf // @grafana/grafana-backend-group
@@ -365,22 +365,22 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.22.0 // indirect
github.com/blang/semver v3.5.1+incompatible // indirect
github.com/blevesearch/geo v0.2.4 // indirect
github.com/blevesearch/go-faiss v1.0.26 // indirect
github.com/blevesearch/geo v0.1.20 // indirect
github.com/blevesearch/go-faiss v1.0.25 // indirect
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
github.com/blevesearch/gtreap v0.1.1 // indirect
github.com/blevesearch/mmap-go v1.0.4 // indirect
github.com/blevesearch/scorch_segment_api/v2 v2.3.13 // indirect
github.com/blevesearch/scorch_segment_api/v2 v2.3.9 // indirect
github.com/blevesearch/segment v0.9.1 // indirect
github.com/blevesearch/snowballstem v0.9.0 // indirect
github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect
github.com/blevesearch/vellum v1.1.0 // indirect
github.com/blevesearch/zapx/v11 v11.4.2 // indirect
github.com/blevesearch/zapx/v12 v12.4.2 // indirect
github.com/blevesearch/zapx/v13 v13.4.2 // indirect
github.com/blevesearch/zapx/v14 v14.4.2 // indirect
github.com/blevesearch/zapx/v15 v15.4.2 // indirect
github.com/blevesearch/zapx/v16 v16.2.8 // indirect
github.com/blevesearch/zapx/v11 v11.4.1 // indirect
github.com/blevesearch/zapx/v12 v12.4.1 // indirect
github.com/blevesearch/zapx/v13 v13.4.1 // indirect
github.com/blevesearch/zapx/v14 v14.4.1 // indirect
github.com/blevesearch/zapx/v15 v15.4.1 // indirect
github.com/blevesearch/zapx/v16 v16.2.2 // indirect
github.com/bluele/gcache v0.0.2 // indirect
github.com/blugelabs/ice v1.0.0 // indirect
github.com/blugelabs/ice/v2 v2.0.1 // indirect
@@ -443,6 +443,7 @@ require (
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect
github.com/gomodule/redigo v1.8.9 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/cel-go v0.26.1 // indirect
+24 -22
View File
@@ -931,14 +931,14 @@ github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdn
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/blevesearch/bleve/v2 v2.5.7 h1:2d9YrL5zrX5EBBW++GOaEKjE+NPWeZGaX77IM26m1Z8=
github.com/blevesearch/bleve/v2 v2.5.7/go.mod h1:yj0NlS7ocGC4VOSAedqDDMktdh2935v2CSWOCDMHdSA=
github.com/blevesearch/bleve_index_api v1.3.0 h1:DsMpWVjFNlBw9/6pyWf59XoqcAkhHj3H0UWiQsavb6E=
github.com/blevesearch/bleve_index_api v1.3.0/go.mod h1:xvd48t5XMeeioWQ5/jZvgLrV98flT2rdvEJ3l/ki4Ko=
github.com/blevesearch/geo v0.2.4 h1:ECIGQhw+QALCZaDcogRTNSJYQXRtC8/m8IKiA706cqk=
github.com/blevesearch/geo v0.2.4/go.mod h1:K56Q33AzXt2YExVHGObtmRSFYZKYGv0JEN5mdacJJR8=
github.com/blevesearch/go-faiss v1.0.26 h1:4dRLolFgjPyjkaXwff4NfbZFdE/dfywbzDqporeQvXI=
github.com/blevesearch/go-faiss v1.0.26/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk=
github.com/blevesearch/bleve/v2 v2.5.0 h1:HzYqBy/5/M9Ul9ESEmXzN/3Jl7YpmWBdHM/+zzv/3k4=
github.com/blevesearch/bleve/v2 v2.5.0/go.mod h1:PcJzTPnEynO15dCf9isxOga7YFRa/cMSsbnRwnszXUk=
github.com/blevesearch/bleve_index_api v1.2.7 h1:c8r9vmbaYQroAMSGag7zq5gEVPiuXrUQDqfnj7uYZSY=
github.com/blevesearch/bleve_index_api v1.2.7/go.mod h1:rKQDl4u51uwafZxFrPD1R7xFOwKnzZW7s/LSeK4lgo0=
github.com/blevesearch/geo v0.1.20 h1:paaSpu2Ewh/tn5DKn/FB5SzvH0EWupxHEIwbCk/QPqM=
github.com/blevesearch/geo v0.1.20/go.mod h1:DVG2QjwHNMFmjo+ZgzrIq2sfCh6rIHzy9d9d0B59I6w=
github.com/blevesearch/go-faiss v1.0.25 h1:lel1rkOUGbT1CJ0YgzKwC7k+XH0XVBHnCVWahdCXk4U=
github.com/blevesearch/go-faiss v1.0.25/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk=
github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo=
github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M=
github.com/blevesearch/gtreap v0.1.1 h1:2JWigFrzDMR+42WGIN/V2p0cUvn4UP3C4Q5nmaZGW8Y=
@@ -947,8 +947,8 @@ github.com/blevesearch/mmap-go v1.0.2/go.mod h1:ol2qBqYaOUsGdm7aRMRrYGgPvnwLe6Y+
github.com/blevesearch/mmap-go v1.0.3/go.mod h1:pYvKl/grLQrBxuaRYgoTssa4rVujYYeenDp++2E+yvs=
github.com/blevesearch/mmap-go v1.0.4 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCDPWmc=
github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs=
github.com/blevesearch/scorch_segment_api/v2 v2.3.13 h1:ZPjv/4VwWvHJZKeMSgScCapOy8+DdmsmRyLmSB88UoY=
github.com/blevesearch/scorch_segment_api/v2 v2.3.13/go.mod h1:ENk2LClTehOuMS8XzN3UxBEErYmtwkE7MAArFTXs9Vc=
github.com/blevesearch/scorch_segment_api/v2 v2.3.9 h1:X6nJXnNHl7nasXW+U6y2Ns2Aw8F9STszkYkyBfQ+p0o=
github.com/blevesearch/scorch_segment_api/v2 v2.3.9/go.mod h1:IrzspZlVjhf4X29oJiEhBxEteTqOY9RlYlk1lCmYHr4=
github.com/blevesearch/segment v0.9.0/go.mod h1:9PfHYUdQCgHktBgvtUOF4x+pc4/l8rdH0u5spnW85UQ=
github.com/blevesearch/segment v0.9.1 h1:+dThDy+Lvgj5JMxhmOVlgFfkUtZV2kw49xax4+jTfSU=
github.com/blevesearch/segment v0.9.1/go.mod h1:zN21iLm7+GnBHWTao9I+Au/7MBiL8pPFtJBJTsk6kQw=
@@ -960,18 +960,18 @@ github.com/blevesearch/vellum v1.0.5/go.mod h1:atE0EH3fvk43zzS7t1YNdNC7DbmcC3uz+
github.com/blevesearch/vellum v1.0.7/go.mod h1:doBZpmRhwTsASB4QdUZANlJvqVAUdUyX0ZK7QJCTeBE=
github.com/blevesearch/vellum v1.1.0 h1:CinkGyIsgVlYf8Y2LUQHvdelgXr6PYuvoDIajq6yR9w=
github.com/blevesearch/vellum v1.1.0/go.mod h1:QgwWryE8ThtNPxtgWJof5ndPfx0/YMBh+W2weHKPw8Y=
github.com/blevesearch/zapx/v11 v11.4.2 h1:l46SV+b0gFN+Rw3wUI1YdMWdSAVhskYuvxlcgpQFljs=
github.com/blevesearch/zapx/v11 v11.4.2/go.mod h1:4gdeyy9oGa/lLa6D34R9daXNUvfMPZqUYjPwiLmekwc=
github.com/blevesearch/zapx/v12 v12.4.2 h1:fzRbhllQmEMUuAQ7zBuMvKRlcPA5ESTgWlDEoB9uQNE=
github.com/blevesearch/zapx/v12 v12.4.2/go.mod h1:TdFmr7afSz1hFh/SIBCCZvcLfzYvievIH6aEISCte58=
github.com/blevesearch/zapx/v13 v13.4.2 h1:46PIZCO/ZuKZYgxI8Y7lOJqX3Irkc3N8W82QTK3MVks=
github.com/blevesearch/zapx/v13 v13.4.2/go.mod h1:knK8z2NdQHlb5ot/uj8wuvOq5PhDGjNYQQy0QDnopZk=
github.com/blevesearch/zapx/v14 v14.4.2 h1:2SGHakVKd+TrtEqpfeq8X+So5PShQ5nW6GNxT7fWYz0=
github.com/blevesearch/zapx/v14 v14.4.2/go.mod h1:rz0XNb/OZSMjNorufDGSpFpjoFKhXmppH9Hi7a877D8=
github.com/blevesearch/zapx/v15 v15.4.2 h1:sWxpDE0QQOTjyxYbAVjt3+0ieu8NCE0fDRaFxEsp31k=
github.com/blevesearch/zapx/v15 v15.4.2/go.mod h1:1pssev/59FsuWcgSnTa0OeEpOzmhtmr/0/11H0Z8+Nw=
github.com/blevesearch/zapx/v16 v16.2.8 h1:SlnzF0YGtSlrsOE3oE7EgEX6BIepGpeqxs1IjMbHLQI=
github.com/blevesearch/zapx/v16 v16.2.8/go.mod h1:murSoCJPCk25MqURrcJaBQ1RekuqSCSfMjXH4rHyA14=
github.com/blevesearch/zapx/v11 v11.4.1 h1:qFCPlFbsEdwbbckJkysptSQOsHn4s6ZOHL5GMAIAVHA=
github.com/blevesearch/zapx/v11 v11.4.1/go.mod h1:qNOGxIqdPC1MXauJCD9HBG487PxviTUUbmChFOAosGs=
github.com/blevesearch/zapx/v12 v12.4.1 h1:K77bhypII60a4v8mwvav7r4IxWA8qxhNjgF9xGdb9eQ=
github.com/blevesearch/zapx/v12 v12.4.1/go.mod h1:QRPrlPOzAxBNMI0MkgdD+xsTqx65zbuPr3Ko4Re49II=
github.com/blevesearch/zapx/v13 v13.4.1 h1:EnkEMZFUK0lsW/jOJJF2xOcp+W8TjEsyeN5BeAZEYYE=
github.com/blevesearch/zapx/v13 v13.4.1/go.mod h1:e6duBMlCvgbH9rkzNMnUa9hRI9F7ri2BRcHfphcmGn8=
github.com/blevesearch/zapx/v14 v14.4.1 h1:G47kGCshknBZzZAtjcnIAMn3oNx8XBLxp8DMq18ogyE=
github.com/blevesearch/zapx/v14 v14.4.1/go.mod h1:O7sDxiaL2r2PnCXbhh1Bvm7b4sP+jp4unE9DDPWGoms=
github.com/blevesearch/zapx/v15 v15.4.1 h1:B5IoTMUCEzFdc9FSQbhVOxAY+BO17c05866fNruiI7g=
github.com/blevesearch/zapx/v15 v15.4.1/go.mod h1:b/MreHjYeQoLjyY2+UaM0hGZZUajEbE0xhnr1A2/Q6Y=
github.com/blevesearch/zapx/v16 v16.2.2 h1:MifKJVRTEhMTgSlle2bDRTb39BGc9jXFRLPZc6r0Rzk=
github.com/blevesearch/zapx/v16 v16.2.2/go.mod h1:B9Pk4G1CqtErgQV9DyCSA9Lb7WZe4olYfGw7fVDZ4sk=
github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw=
github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0=
github.com/blugelabs/bluge v0.2.2 h1:gat8CqE6P6tOgeX30XGLOVNTC26cpM2RWVcreXWtYcM=
@@ -1442,6 +1442,8 @@ github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2V
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 h1:gtexQ/VGyN+VVFRXSFiguSNcXmS6rkKT+X7FdIrTtfo=
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ=
+2 -27
View File
@@ -520,40 +520,14 @@ github.com/benbjohnson/immutable v0.4.0 h1:CTqXbEerYso8YzVPxmWxh2gnoRQbbB9X1quUC
github.com/benbjohnson/immutable v0.4.0/go.mod h1:iAr8OjJGLnLmVUr9MZ/rz4PWUy6Ouc2JLYuMArmvAJM=
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY=
github.com/blevesearch/bleve/v2 v2.5.7 h1:2d9YrL5zrX5EBBW++GOaEKjE+NPWeZGaX77IM26m1Z8=
github.com/blevesearch/bleve/v2 v2.5.7/go.mod h1:yj0NlS7ocGC4VOSAedqDDMktdh2935v2CSWOCDMHdSA=
github.com/blevesearch/bleve_index_api v1.2.8/go.mod h1:rKQDl4u51uwafZxFrPD1R7xFOwKnzZW7s/LSeK4lgo0=
github.com/blevesearch/bleve_index_api v1.2.11 h1:bXQ54kVuwP8hdrXUSOnvTQfgK0KI1+f9A0ITJT8tX1s=
github.com/blevesearch/bleve_index_api v1.2.11/go.mod h1:rKQDl4u51uwafZxFrPD1R7xFOwKnzZW7s/LSeK4lgo0=
github.com/blevesearch/bleve_index_api v1.3.0 h1:DsMpWVjFNlBw9/6pyWf59XoqcAkhHj3H0UWiQsavb6E=
github.com/blevesearch/bleve_index_api v1.3.0/go.mod h1:xvd48t5XMeeioWQ5/jZvgLrV98flT2rdvEJ3l/ki4Ko=
github.com/blevesearch/geo v0.2.4 h1:ECIGQhw+QALCZaDcogRTNSJYQXRtC8/m8IKiA706cqk=
github.com/blevesearch/geo v0.2.4/go.mod h1:K56Q33AzXt2YExVHGObtmRSFYZKYGv0JEN5mdacJJR8=
github.com/blevesearch/go-faiss v1.0.26/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk=
github.com/blevesearch/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:kDy+zgJFJJoJYBvdfBSiZYBbdsUL0XcjHYWezpQBGPA=
github.com/blevesearch/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:9eJDeqxJ3E7WnLebQUlPD7ZjSce7AnDb9vjGmMCbD0A=
github.com/blevesearch/goleveldb v1.0.1 h1:iAtV2Cu5s0GD1lwUiekkFHe2gTMCCNVj2foPclDLIFI=
github.com/blevesearch/goleveldb v1.0.1/go.mod h1:WrU8ltZbIp0wAoig/MHbrPCXSOLpe79nz5lv5nqfYrQ=
github.com/blevesearch/scorch_segment_api/v2 v2.3.10/go.mod h1:Z3e6ChN3qyN35yaQpl00MfI5s8AxUJbpTR/DL8QOQ+8=
github.com/blevesearch/scorch_segment_api/v2 v2.3.13 h1:ZPjv/4VwWvHJZKeMSgScCapOy8+DdmsmRyLmSB88UoY=
github.com/blevesearch/scorch_segment_api/v2 v2.3.13/go.mod h1:ENk2LClTehOuMS8XzN3UxBEErYmtwkE7MAArFTXs9Vc=
github.com/blevesearch/snowball v0.6.1 h1:cDYjn/NCH+wwt2UdehaLpr2e4BwLIjN4V/TdLsL+B5A=
github.com/blevesearch/snowball v0.6.1/go.mod h1:ZF0IBg5vgpeoUhnMza2v0A/z8m1cWPlwhke08LpNusg=
github.com/blevesearch/stempel v0.2.0 h1:CYzVPaScODMvgE9o+kf6D4RJ/VRomyi9uHF+PtB+Afc=
github.com/blevesearch/stempel v0.2.0/go.mod h1:wjeTHqQv+nQdbPuJ/YcvOjTInA2EIc6Ks1FoSUzSLvc=
github.com/blevesearch/vellum v1.0.10/go.mod h1:ul1oT0FhSMDIExNjIxHqJoGpVrBpKCdgDQNxfqgJt7k=
github.com/blevesearch/zapx/v11 v11.4.2 h1:l46SV+b0gFN+Rw3wUI1YdMWdSAVhskYuvxlcgpQFljs=
github.com/blevesearch/zapx/v11 v11.4.2/go.mod h1:4gdeyy9oGa/lLa6D34R9daXNUvfMPZqUYjPwiLmekwc=
github.com/blevesearch/zapx/v12 v12.4.2 h1:fzRbhllQmEMUuAQ7zBuMvKRlcPA5ESTgWlDEoB9uQNE=
github.com/blevesearch/zapx/v12 v12.4.2/go.mod h1:TdFmr7afSz1hFh/SIBCCZvcLfzYvievIH6aEISCte58=
github.com/blevesearch/zapx/v13 v13.4.2 h1:46PIZCO/ZuKZYgxI8Y7lOJqX3Irkc3N8W82QTK3MVks=
github.com/blevesearch/zapx/v13 v13.4.2/go.mod h1:knK8z2NdQHlb5ot/uj8wuvOq5PhDGjNYQQy0QDnopZk=
github.com/blevesearch/zapx/v14 v14.4.2 h1:2SGHakVKd+TrtEqpfeq8X+So5PShQ5nW6GNxT7fWYz0=
github.com/blevesearch/zapx/v14 v14.4.2/go.mod h1:rz0XNb/OZSMjNorufDGSpFpjoFKhXmppH9Hi7a877D8=
github.com/blevesearch/zapx/v15 v15.4.2 h1:sWxpDE0QQOTjyxYbAVjt3+0ieu8NCE0fDRaFxEsp31k=
github.com/blevesearch/zapx/v15 v15.4.2/go.mod h1:1pssev/59FsuWcgSnTa0OeEpOzmhtmr/0/11H0Z8+Nw=
github.com/blevesearch/zapx/v16 v16.2.8 h1:SlnzF0YGtSlrsOE3oE7EgEX6BIepGpeqxs1IjMbHLQI=
github.com/blevesearch/zapx/v16 v16.2.8/go.mod h1:murSoCJPCk25MqURrcJaBQ1RekuqSCSfMjXH4rHyA14=
github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0=
github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE=
github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I=
@@ -1024,6 +998,8 @@ github.com/grafana/prometheus-alertmanager v0.25.1-0.20250331083058-4563aec7a975
github.com/grafana/prometheus-alertmanager v0.25.1-0.20250331083058-4563aec7a975/go.mod h1:FGdGvhI40Dq+CTQaSzK9evuve774cgOUdGfVO04OXkw=
github.com/grafana/prometheus-alertmanager v0.25.1-0.20250604130045-92c8f6389b36 h1:AjZ58JRw1ZieFH/SdsddF5BXtsDKt5kSrKNPWrzYz3Y=
github.com/grafana/prometheus-alertmanager v0.25.1-0.20250604130045-92c8f6389b36/go.mod h1:O/QP1BCm0HHIzbKvgMzqb5sSyH88rzkFk84F4TfJjBU=
github.com/grafana/prometheus-alertmanager v0.25.1-0.20260112162805-d29cc9cf7f0f h1:9tRhudagkQO2s61SLFLSziIdCm7XlkfypVKDxpcHokg=
github.com/grafana/prometheus-alertmanager v0.25.1-0.20260112162805-d29cc9cf7f0f/go.mod h1:AsVdCBeDFN9QbgpJg+8voDAcgsW0RmNvBd70ecMMdC0=
github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU=
github.com/grafana/pyroscope/api v1.2.1-0.20250415190842-3ff7247547ae/go.mod h1:6CJ1uXmLZ13ufpO9xE4pST+DyaBt0uszzrV0YnoaVLQ=
github.com/grafana/sqlds/v4 v4.2.4/go.mod h1:BQRjUG8rOqrBI4NAaeoWrIMuoNgfi8bdhCJ+5cgEfLU=
@@ -1116,7 +1092,6 @@ github.com/jon-whit/go-grpc-prometheus v1.4.0/go.mod h1:iTPm+Iuhh3IIqR0iGZ91JJEg
github.com/joncrlsn/dque v0.0.0-20211108142734-c2ef48c5192a h1:sfe532Ipn7GX0V6mHdynBk393rDmqgI0QmjLK7ct7TU=
github.com/joncrlsn/dque v0.0.0-20211108142734-c2ef48c5192a/go.mod h1:dNKs71rs2VJGBAmttu7fouEsRQlRjxy0p1Sx+T5wbpY=
github.com/josephspurrier/goversioninfo v1.4.0/go.mod h1:JWzv5rKQr+MmW+LvM412ToT/IkYDZjaclF2pKDss8IY=
github.com/json-iterator/go v0.0.0-20171115153421-f7279a603ede/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o=
github.com/jsternberg/zap-logfmt v1.3.0 h1:z1n1AOHVVydOOVuyphbOKyR4NICDQFiJMn1IK5hVQ5Y=
github.com/jsternberg/zap-logfmt v1.3.0/go.mod h1:N3DENp9WNmCZxvkBD/eReWwz1149BK6jEN9cQ4fNwZE=
-1
View File
@@ -82,7 +82,6 @@ module.exports = {
// Decoupled plugins run their own tests so ignoring them here.
'<rootDir>/public/app/plugins/datasource/azuremonitor',
'<rootDir>/public/app/plugins/datasource/cloud-monitoring',
'<rootDir>/public/app/plugins/datasource/elasticsearch',
'<rootDir>/public/app/plugins/datasource/grafana-postgresql-datasource',
'<rootDir>/public/app/plugins/datasource/grafana-pyroscope-datasource',
'<rootDir>/public/app/plugins/datasource/grafana-testdata-datasource',
+2 -2
View File
@@ -293,8 +293,8 @@
"@grafana/plugin-ui": "^0.11.1",
"@grafana/prometheus": "workspace:*",
"@grafana/runtime": "workspace:*",
"@grafana/scenes": "6.52.2",
"@grafana/scenes-react": "6.52.2",
"@grafana/scenes": "v6.52.1",
"@grafana/scenes-react": "v6.52.1",
"@grafana/schema": "workspace:*",
"@grafana/sql": "workspace:*",
"@grafana/ui": "workspace:*",
+1 -10
View File
@@ -35,14 +35,6 @@
},
"./test": {
"@grafana-app/source": "./test/index.ts"
},
"./themes/schema.generated.json": {
"@grafana-app/source": "./src/themes/schema.generated.json",
"default": "./dist/esm/themes/schema.generated.json"
},
"./themes/definitions/*.json": {
"@grafana-app/source": "./src/themes/themeDefinitions/*.json",
"default": "./dist/esm/themes/themeDefinitions/*.json"
}
},
"publishConfig": {
@@ -60,7 +52,7 @@
"typecheck": "tsc --emitDeclarationOnly false --noEmit",
"prepack": "cp package.json package.json.bak && node ../../scripts/prepare-npm-package.js",
"postpack": "mv package.json.bak package.json",
"themes-schema": "tsx ./scripts/generateSchema.ts"
"themes-schema": "tsx ./src/themes/scripts/generateSchema.ts"
},
"dependencies": {
"@braintree/sanitize-url": "7.0.1",
@@ -110,7 +102,6 @@
"react-dom": "18.3.1",
"rimraf": "6.0.1",
"rollup": "^4.22.4",
"rollup-plugin-copy": "3.5.0",
"rollup-plugin-esbuild": "6.2.1",
"rollup-plugin-node-externals": "^8.0.0",
"tsx": "^4.21.0",
+2 -21
View File
@@ -1,40 +1,21 @@
import json from '@rollup/plugin-json';
import { createRequire } from 'node:module';
import copy from 'rollup-plugin-copy';
import { entryPoint, plugins, esmOutput, cjsOutput } from '../rollup.config.parts';
const rq = createRequire(import.meta.url);
const pkg = rq('./package.json');
const grafanaDataPlugins = [
...plugins,
copy({
targets: [
{
src: 'src/themes/schema.generated.json',
dest: 'dist/esm/',
},
{
src: 'src/themes/themeDefinitions/*.json',
dest: 'dist/esm/',
},
],
flatten: false,
}),
json(),
];
export default [
{
input: entryPoint,
plugins: grafanaDataPlugins,
plugins: [...plugins, json()],
output: [cjsOutput(pkg, 'grafana-data'), esmOutput(pkg, 'grafana-data')],
treeshake: false,
},
{
input: 'src/unstable.ts',
plugins: grafanaDataPlugins,
plugins: [...plugins, json()],
output: [cjsOutput(pkg, 'grafana-data'), esmOutput(pkg, 'grafana-data')],
treeshake: false,
},
@@ -1,22 +0,0 @@
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { NewThemeOptionsSchema } from '../src/themes/createTheme';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const jsonOut = path.join(__dirname, '..', 'src', 'themes', 'schema.generated.json');
fs.writeFileSync(
jsonOut,
JSON.stringify(
NewThemeOptionsSchema.toJSONSchema({
target: 'draft-07',
}),
undefined,
2
)
);
console.log('Successfully generated theme schema');
+1 -1
View File
@@ -844,6 +844,7 @@ export {
DataLinkConfigOrigin,
SupportedTransformationType,
type InternalDataLink,
type LinkTarget,
type LinkModel,
type LinkModelSupplier,
VariableOrigin,
@@ -851,7 +852,6 @@ export {
VariableSuggestionsScope,
OneClickMode,
} from './types/dataLink';
export { type LinkTarget } from './types/linkTarget';
export {
type Action,
type ActionModel,
@@ -93,6 +93,7 @@ export { DataTransformerID } from '../transformations/transformers/ids';
export { mergeTransformer } from '../transformations/transformers/merge';
export { getThemeById } from '../themes/registry';
export * as experimentalThemeDefinitions from '../themes/themeDefinitions';
export { GrafanaEdition } from '../types/config';
export { SIPrefix } from '../valueFormats/symbolFormatters';
+1 -27
View File
@@ -1,18 +1,7 @@
import { Registry, RegistryItem } from '../utils/Registry';
import { createTheme, NewThemeOptionsSchema } from './createTheme';
import aubergine from './themeDefinitions/aubergine.json';
import debug from './themeDefinitions/debug.json';
import desertbloom from './themeDefinitions/desertbloom.json';
import gildedgrove from './themeDefinitions/gildedgrove.json';
import gloom from './themeDefinitions/gloom.json';
import mars from './themeDefinitions/mars.json';
import matrix from './themeDefinitions/matrix.json';
import sapphiredusk from './themeDefinitions/sapphiredusk.json';
import synthwave from './themeDefinitions/synthwave.json';
import tron from './themeDefinitions/tron.json';
import victorian from './themeDefinitions/victorian.json';
import zen from './themeDefinitions/zen.json';
import * as extraThemes from './themeDefinitions';
import { GrafanaTheme2 } from './types';
export interface ThemeRegistryItem extends RegistryItem {
@@ -20,21 +9,6 @@ export interface ThemeRegistryItem extends RegistryItem {
build: () => GrafanaTheme2;
}
const extraThemes: { [key: string]: unknown } = {
aubergine,
debug,
desertbloom,
gildedgrove,
gloom,
mars,
matrix,
sapphiredusk,
synthwave,
tron,
victorian,
zen,
};
/**
* @internal
* Only for internal use, never use this from a plugin
@@ -0,0 +1,19 @@
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { NewThemeOptionsSchema } from '../createTheme';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
fs.writeFileSync(
path.join(__dirname, '../schema.generated.json'),
JSON.stringify(
NewThemeOptionsSchema.toJSONSchema({
target: 'draft-07',
}),
undefined,
2
)
);
@@ -0,0 +1,12 @@
export { default as aubergine } from './aubergine.json';
export { default as debug } from './debug.json';
export { default as desertbloom } from './desertbloom.json';
export { default as gildedgrove } from './gildedgrove.json';
export { default as mars } from './mars.json';
export { default as matrix } from './matrix.json';
export { default as sapphiredusk } from './sapphiredusk.json';
export { default as synthwave } from './synthwave.json';
export { default as tron } from './tron.json';
export { default as victorian } from './victorian.json';
export { default as zen } from './zen.json';
export { default as gloom } from './gloom.json';
+2 -1
View File
@@ -1,6 +1,5 @@
import { ScopedVars } from './ScopedVars';
import { ExploreCorrelationHelperData, ExplorePanelsState } from './explore';
import { LinkTarget } from './linkTarget';
import { InterpolateFunction } from './panel';
import { DataQuery } from './query';
import { TimeRange } from './time';
@@ -89,6 +88,8 @@ export interface InternalDataLink<T extends DataQuery = any> {
range?: TimeRange;
}
export type LinkTarget = '_blank' | '_self' | undefined;
/**
* Processed Link Model. The values are ready to use
*/
+1 -9
View File
@@ -356,7 +356,7 @@ export interface FeatureToggles {
*/
dashboardScene?: boolean;
/**
* Enables new dashboard layouts
* Enables experimental new dashboard layouts
*/
dashboardNewLayouts?: boolean;
/**
@@ -531,10 +531,6 @@ export interface FeatureToggles {
*/
alertingListViewV2?: boolean;
/**
* Enables the new Alerting navigation structure with improved menu grouping
*/
alertingNavigationV2?: boolean;
/**
* Enables saved searches for alert rules list
*/
alertingSavedSearches?: boolean;
@@ -1255,8 +1251,4 @@ export interface FeatureToggles {
* Enables profiles exemplars support in profiles drilldown
*/
profilesExemplars?: boolean;
/**
* Use synchronized dispatch timer to minimize duplicate notifications across alertmanager HA pods
*/
alertingSyncDispatchTimer?: boolean;
}
@@ -1,4 +0,0 @@
/**
* Target for links - controls whether link opens in new tab or same tab
*/
export type LinkTarget = '_blank' | '_self' | undefined;
+1 -1
View File
@@ -1,7 +1,7 @@
import { ComponentType } from 'react';
import { LinkTarget } from './dataLink';
import { IconName } from './icon';
import { LinkTarget } from './linkTarget';
export interface NavLinkDTO {
id?: string;
-2
View File
@@ -11,7 +11,6 @@ import { DataFrame } from './dataFrame';
import { DataQueryError, DataQueryRequest, DataQueryTimings } from './datasource';
import { FieldConfigSource } from './fieldOverrides';
import { IconName } from './icon';
import { LinkTarget } from './linkTarget';
import { OptionEditorConfig } from './options';
import { PluginMeta } from './plugin';
import { AbsoluteTimeRange, TimeRange, TimeZone } from './time';
@@ -192,7 +191,6 @@ export interface PanelMenuItem {
onClick?: (event: React.MouseEvent) => void;
shortcut?: string;
href?: string;
target?: LinkTarget;
subMenu?: PanelMenuItem[];
}
+1 -1
View File
@@ -9,4 +9,4 @@
* and be subject to the standard policies
*/
export {};
export { default as themeJsonSchema } from './themes/schema.generated.json';
+1 -2
View File
@@ -8,8 +8,7 @@
"emitDeclarationOnly": true,
"isolatedModules": true,
"rootDirs": ["."],
"moduleResolution": "bundler",
"resolveJsonModule": true
"moduleResolution": "bundler"
},
"exclude": ["dist/**/*"],
"include": [
@@ -16,11 +16,8 @@ export async function initOpenFeature() {
* to allow for overrides https://github.com/open-feature/js-sdk-contrib/tree/main/libs/providers/multi-provider
*/
const subPath = config.appSubUrl || '';
const baseUrl = `${subPath}/apis/features.grafana.app/v0alpha1/namespaces/${config.namespace}`;
const ofProvider = new OFREPWebProvider({
baseUrl: baseUrl,
baseUrl: '/apis/features.grafana.app/v0alpha1/namespaces/' + config.namespace,
pollInterval: -1, // disable polling
timeoutMs: 5_000,
});
@@ -10,7 +10,7 @@
import * as common from '@grafana/schema';
export const pluginVersion = "%VERSION%";
export const pluginVersion = "12.4.0-pre";
export type BucketAggregation = (DateHistogram | Histogram | Terms | Filters | GeoHashGrid | Nested);
+12 -10
View File
@@ -42,7 +42,7 @@ func (r *converter) asDataSource(ds *datasources.DataSource) (*datasourceV0.Data
Generation: int64(ds.Version),
},
Spec: datasourceV0.UnstructuredSpec{},
Secure: ToInlineSecureValues(ds.Type, ds.UID, maps.Keys(ds.SecureJsonData)),
Secure: ToInlineSecureValues("", ds.UID, maps.Keys(ds.SecureJsonData)),
}
obj.UID = gapiutil.CalculateClusterWideUID(obj)
obj.Spec.SetTitle(ds.Name).
@@ -82,18 +82,11 @@ func (r *converter) asDataSource(ds *datasources.DataSource) (*datasourceV0.Data
// ToInlineSecureValues converts secure json into InlineSecureValues with reference names
// The names are predictable and can be used while we implement dual writing for secrets
func ToInlineSecureValues(dsType string, dsUID string, keys iter.Seq[string]) common.InlineSecureValues {
func ToInlineSecureValues(_ string, dsUID string, keys iter.Seq[string]) common.InlineSecureValues {
values := make(common.InlineSecureValues)
for k := range keys {
h := sha256.New()
h.Write([]byte(dsType)) // plugin id
h.Write([]byte("|"))
h.Write([]byte(dsUID)) // unique identifier
h.Write([]byte("|"))
h.Write([]byte(k)) // property name
n := hex.EncodeToString(h.Sum(nil))
values[k] = common.InlineSecureValue{
Name: "ds-" + n[0:10], // predictable name for dual writing
Name: getLegacySecureValueName(dsUID, k),
}
}
if len(values) == 0 {
@@ -102,6 +95,15 @@ func ToInlineSecureValues(dsType string, dsUID string, keys iter.Seq[string]) co
return values
}
func getLegacySecureValueName(dsUID string, key string) string {
h := sha256.New()
h.Write([]byte(dsUID)) // unique identifier
h.Write([]byte("|"))
h.Write([]byte(key)) // property name
n := hex.EncodeToString(h.Sum(nil))
return "ds-" + n[0:10] // predictable name for dual writing
}
func (r *converter) toAddCommand(ds *datasourceV0.DataSource) (*datasources.AddDataSourceCommand, error) {
if r.group != "" && ds.APIVersion != "" && !strings.HasPrefix(ds.APIVersion, r.group) {
return nil, fmt.Errorf("expecting APIGroup: %s", r.group)
@@ -11,9 +11,11 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/rest"
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
"github.com/grafana/grafana/pkg/apimachinery/utils"
"github.com/grafana/grafana/pkg/apis/datasource/v0alpha1"
"github.com/grafana/grafana/pkg/infra/metrics/metricutil"
"github.com/grafana/grafana/pkg/storage/legacysql/dualwrite"
)
var (
@@ -90,6 +92,20 @@ func (s *legacyStorage) Create(ctx context.Context, obj runtime.Object, createVa
if !ok {
return nil, fmt.Errorf("expected a datasource object")
}
// Verify the secure value commands
for _, v := range ds.Secure {
if v.Create.IsZero() {
return nil, fmt.Errorf("secure values must use create when creating a new datasource")
}
if v.Remove {
return nil, fmt.Errorf("secure values can not use remove when creating a new datasource")
}
if v.Name != "" {
return nil, fmt.Errorf("secure values can not specify a name when creating a new datasource")
}
}
return s.datasources.CreateDataSource(ctx, ds)
}
@@ -122,6 +138,26 @@ func (s *legacyStorage) Update(ctx context.Context, name string, objInfo rest.Up
return nil, false, fmt.Errorf("expected a datasource object (old)")
}
// Expose any secure value changes to the dual writer
var secureChanges common.InlineSecureValues
for k, v := range ds.Secure {
if v.Remove || v.Create != "" {
if secureChanges == nil {
secureChanges = make(common.InlineSecureValues)
}
secureChanges[k] = v
dualwrite.SetUpdatedSecureValues(ctx, ds.Secure)
continue
}
// The legacy store must use fixed names generated by the internal system
// we can not support external shared secrets when using the SQL backing for datasources
validName := getLegacySecureValueName(name, k)
if v.Name != validName {
return nil, false, fmt.Errorf("invalid secure value name %q, expected %q", v.Name, validName)
}
}
// Keep all the old secure values
if len(oldDS.Secure) > 0 {
for k, v := range oldDS.Secure {
+9 -2
View File
@@ -30,6 +30,7 @@ import (
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/apiserver/builder"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/storage/unified/apistore"
"github.com/grafana/grafana/pkg/tsdb/grafana-testdata-datasource/kinds"
)
@@ -102,10 +103,10 @@ func RegisterAPIService(
datasources.GetDatasourceProvider(pluginJSON),
contextProvider,
accessControl,
//nolint:staticcheck // not yet migrated to OpenFeature
DataSourceAPIBuilderConfig{
//nolint:staticcheck // not yet migrated to OpenFeature
LoadQueryTypes: features.IsEnabledGlobally(featuremgmt.FlagDatasourceQueryTypes),
UseDualWriter: false,
UseDualWriter: features.IsEnabledGlobally(featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs),
},
)
if err != nil {
@@ -224,6 +225,12 @@ func (b *DataSourceAPIBuilder) AllowedV0Alpha1Resources() []string {
}
func (b *DataSourceAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver.APIGroupInfo, opts builder.APIGroupOptions) error {
opts.StorageOptsRegister(b.datasourceResourceInfo.GroupResource(), apistore.StorageOptions{
EnableFolderSupport: false,
Scheme: opts.Scheme, // allows for generic Type applied to multiple groups
})
storage := map[string]rest.Storage{}
// Register the raw datasource connection
@@ -33,7 +33,7 @@
},
"secure": {
"password": {
"name": "ds-d5c1b093af"
"name": "ds-0d27eff323"
}
}
}
@@ -22,10 +22,10 @@
},
"secure": {
"extra": {
"name": "ds-bb8b5d8b32"
"name": "ds-6ed1b76e5d"
},
"password": {
"name": "ds-973a1eb29d"
"name": "ds-edc8fde0ac"
}
}
}
@@ -60,7 +60,7 @@ func (s *LocalInlineSecureValueService) CanReference(ctx context.Context, owner
}
if owner.APIGroup == "" || owner.APIVersion == "" || owner.Kind == "" || owner.Name == "" {
return fmt.Errorf("owner reference must have a valid API group, API version, kind and name")
return fmt.Errorf("owner reference must have a valid API group, API version, kind and name [CanReference]")
}
if len(names) == 0 {
@@ -167,7 +167,7 @@ func (s *LocalInlineSecureValueService) verifyOwnerAndAuth(ctx context.Context,
}
if owner.Namespace == "" || owner.APIGroup == "" || owner.APIVersion == "" || owner.Kind == "" || owner.Name == "" {
return nil, fmt.Errorf("owner reference must have a valid API group, API version, kind, namespace and name")
return nil, fmt.Errorf("owner reference must have a valid API group, API version, kind, namespace and name [verifyOwnerAndAuth:%+v]", owner)
}
return authInfo, nil
+3 -19
View File
@@ -574,8 +574,8 @@ var (
},
{
Name: "dashboardNewLayouts",
Description: "Enables new dashboard layouts",
Stage: FeatureStagePublicPreview,
Description: "Enables experimental new dashboard layouts",
Stage: FeatureStageExperimental,
FrontendOnly: false, // The restore backend feature changes behavior based on this flag
Owner: grafanaDashboardsSquad,
},
@@ -879,13 +879,6 @@ var (
Owner: grafanaAlertingSquad,
FrontendOnly: true,
},
{
Name: "alertingNavigationV2",
Description: "Enables the new Alerting navigation structure with improved menu grouping",
Stage: FeatureStageExperimental,
Owner: grafanaAlertingSquad,
FrontendOnly: false,
},
{
Name: "alertingSavedSearches",
Description: "Enables saved searches for alert rules list",
@@ -988,8 +981,7 @@ var (
Stage: FeatureStageDeprecated,
Owner: grafanaPartnerPluginsSquad,
Expression: "true", // Enabled by default for now
},
{
}, {
Name: "alertingFilterV2",
Description: "Enable the new alerting search experience",
Stage: FeatureStageExperimental,
@@ -2077,14 +2069,6 @@ var (
Owner: grafanaObservabilityTracesAndProfilingSquad,
FrontendOnly: false,
},
{
Name: "alertingSyncDispatchTimer",
Description: "Use synchronized dispatch timer to minimize duplicate notifications across alertmanager HA pods",
Stage: FeatureStageExperimental,
Owner: grafanaAlertingSquad,
RequiresRestart: true,
HideFromDocs: true,
},
}
)
+1 -3
View File
@@ -79,7 +79,7 @@ annotationPermissionUpdate,GA,@grafana/identity-access-team,false,false,false
dashboardSceneForViewers,GA,@grafana/dashboards-squad,false,false,true
dashboardSceneSolo,GA,@grafana/dashboards-squad,false,false,true
dashboardScene,GA,@grafana/dashboards-squad,false,false,true
dashboardNewLayouts,preview,@grafana/dashboards-squad,false,false,false
dashboardNewLayouts,experimental,@grafana/dashboards-squad,false,false,false
dashboardUndoRedo,experimental,@grafana/dashboards-squad,false,false,true
unlimitedLayoutsNesting,experimental,@grafana/dashboards-squad,false,false,true
drilldownRecommendations,experimental,@grafana/dashboards-squad,false,false,true
@@ -121,7 +121,6 @@ dashboardLibrary,experimental,@grafana/sharing-squad,false,false,false
suggestedDashboards,experimental,@grafana/sharing-squad,false,false,false
dashboardTemplates,preview,@grafana/sharing-squad,false,false,false
alertingListViewV2,privatePreview,@grafana/alerting-squad,false,false,true
alertingNavigationV2,experimental,@grafana/alerting-squad,false,false,false
alertingSavedSearches,experimental,@grafana/alerting-squad,false,false,true
alertingDisableSendAlertsExternal,experimental,@grafana/alerting-squad,false,false,false
preserveDashboardStateWhenNavigating,experimental,@grafana/dashboards-squad,false,false,false
@@ -281,4 +280,3 @@ multiPropsVariables,experimental,@grafana/dashboards-squad,false,false,true
smoothingTransformation,experimental,@grafana/datapro,false,false,true
secretsManagementAppPlatformAwsKeeper,experimental,@grafana/grafana-operator-experience-squad,false,false,false
profilesExemplars,experimental,@grafana/observability-traces-and-profiling,false,false,false
alertingSyncDispatchTimer,experimental,@grafana/alerting-squad,false,true,false
1 Name Stage Owner requiresDevMode RequiresRestart FrontendOnly
79 dashboardSceneForViewers GA @grafana/dashboards-squad false false true
80 dashboardSceneSolo GA @grafana/dashboards-squad false false true
81 dashboardScene GA @grafana/dashboards-squad false false true
82 dashboardNewLayouts preview experimental @grafana/dashboards-squad false false false
83 dashboardUndoRedo experimental @grafana/dashboards-squad false false true
84 unlimitedLayoutsNesting experimental @grafana/dashboards-squad false false true
85 drilldownRecommendations experimental @grafana/dashboards-squad false false true
121 suggestedDashboards experimental @grafana/sharing-squad false false false
122 dashboardTemplates preview @grafana/sharing-squad false false false
123 alertingListViewV2 privatePreview @grafana/alerting-squad false false true
alertingNavigationV2 experimental @grafana/alerting-squad false false false
124 alertingSavedSearches experimental @grafana/alerting-squad false false true
125 alertingDisableSendAlertsExternal experimental @grafana/alerting-squad false false false
126 preserveDashboardStateWhenNavigating experimental @grafana/dashboards-squad false false false
280 smoothingTransformation experimental @grafana/datapro false false true
281 secretsManagementAppPlatformAwsKeeper experimental @grafana/grafana-operator-experience-squad false false false
282 profilesExemplars experimental @grafana/observability-traces-and-profiling false false false
alertingSyncDispatchTimer experimental @grafana/alerting-squad false true false
+1 -9
View File
@@ -260,7 +260,7 @@ const (
FlagAnnotationPermissionUpdate = "annotationPermissionUpdate"
// FlagDashboardNewLayouts
// Enables new dashboard layouts
// Enables experimental new dashboard layouts
FlagDashboardNewLayouts = "dashboardNewLayouts"
// FlagPdfTables
@@ -371,10 +371,6 @@ const (
// Enables a flow to get started with a new dashboard from a template
FlagDashboardTemplates = "dashboardTemplates"
// FlagAlertingNavigationV2
// Enables the new Alerting navigation structure with improved menu grouping
FlagAlertingNavigationV2 = "alertingNavigationV2"
// FlagAlertingDisableSendAlertsExternal
// Disables the ability to send alerts to an external Alertmanager datasource.
FlagAlertingDisableSendAlertsExternal = "alertingDisableSendAlertsExternal"
@@ -793,8 +789,4 @@ const (
// FlagProfilesExemplars
// Enables profiles exemplars support in profiles drilldown
FlagProfilesExemplars = "profilesExemplars"
// FlagAlertingSyncDispatchTimer
// Use synchronized dispatch timer to minimize duplicate notifications across alertmanager HA pods
FlagAlertingSyncDispatchTimer = "alertingSyncDispatchTimer"
)
+5 -35
View File
@@ -348,18 +348,6 @@
"expression": "true"
}
},
{
"metadata": {
"name": "alertingNavigationV2",
"resourceVersion": "1768320918269",
"creationTimestamp": "2026-01-13T16:15:18Z"
},
"spec": {
"description": "Enables the new Alerting navigation structure with improved menu grouping",
"stage": "experimental",
"codeowner": "@grafana/alerting-squad"
}
},
{
"metadata": {
"name": "alertingNotificationHistory",
@@ -523,20 +511,6 @@
"frontend": true
}
},
{
"metadata": {
"name": "alertingSyncDispatchTimer",
"resourceVersion": "1766161788928",
"creationTimestamp": "2025-12-19T16:29:48Z"
},
"spec": {
"description": "Use synchronized dispatch timer to minimize duplicate notifications across alertmanager HA pods",
"stage": "experimental",
"codeowner": "@grafana/alerting-squad",
"requiresRestart": true,
"hideFromDocs": true
}
},
{
"metadata": {
"name": "alertingTriage",
@@ -688,8 +662,7 @@
"metadata": {
"name": "auditLoggingAppPlatform",
"resourceVersion": "1767013056996",
"creationTimestamp": "2025-12-29T12:57:36Z",
"deletionTimestamp": "2026-01-06T09:18:36Z"
"creationTimestamp": "2025-12-29T12:57:36Z"
},
"spec": {
"description": "Enable audit logging with Kubernetes under app platform",
@@ -1042,15 +1015,12 @@
{
"metadata": {
"name": "dashboardNewLayouts",
"resourceVersion": "1768382835527",
"creationTimestamp": "2024-10-23T08:55:45Z",
"annotations": {
"grafana.app/updatedTimestamp": "2026-01-14 09:27:15.527103 +0000 UTC"
}
"resourceVersion": "1764664939750",
"creationTimestamp": "2024-10-23T08:55:45Z"
},
"spec": {
"description": "Enables new dashboard layouts",
"stage": "preview",
"description": "Enables experimental new dashboard layouts",
"stage": "experimental",
"codeowner": "@grafana/dashboards-squad"
}
},
+2 -1
View File
@@ -54,7 +54,8 @@ func (s *ServiceImpl) getAdminNode(c *contextmodel.ReqContext) (*navtree.NavLink
}
//nolint:staticcheck // not yet migrated to OpenFeature
if c.HasRole(identity.RoleAdmin) &&
s.features.IsEnabledGlobally(featuremgmt.FlagProvisioning) {
(s.cfg.StackID == "" || // show OnPrem even when provisioning is disabled
s.features.IsEnabledGlobally(featuremgmt.FlagProvisioning)) {
generalNodeLinks = append(generalNodeLinks, &navtree.NavLink{
Text: "Provisioning",
Id: "provisioning",
-4
View File
@@ -213,9 +213,6 @@ func (ng *AlertNG) init() error {
SkipVerify: ng.Cfg.Smtp.SkipVerify,
StaticHeaders: ng.Cfg.Smtp.StaticHeaders,
}
runtimeConfig := remoteClient.RuntimeConfig{
DispatchTimer: notifier.GetDispatchTimer(ng.FeatureToggles).String(),
}
cfg := remote.AlertmanagerConfig{
BasicAuthPassword: ng.Cfg.UnifiedAlerting.RemoteAlertmanager.Password,
@@ -225,7 +222,6 @@ func (ng *AlertNG) init() error {
ExternalURL: ng.Cfg.AppURL,
SmtpConfig: smtpCfg,
Timeout: ng.Cfg.UnifiedAlerting.RemoteAlertmanager.Timeout,
RuntimeConfig: runtimeConfig,
}
autogenFn := func(ctx context.Context, logger log.Logger, orgID int64, cfg *definitions.PostableApiAlertingConfig, invalidReceiverAction notifier.InvalidReceiversAction) error {
return notifier.AddAutogenConfig(ctx, logger, ng.store, orgID, cfg, invalidReceiverAction, ng.FeatureToggles)
@@ -33,9 +33,6 @@ const (
// How long we keep silences in the kvstore after they've expired.
silenceRetention = 5 * 24 * time.Hour
// How long we keep flushes in the kvstore after they've expired.
flushRetention = 5 * 24 * time.Hour
)
type AlertingStore interface {
@@ -47,10 +44,8 @@ type AlertingStore interface {
type stateStore interface {
SaveSilences(ctx context.Context, st alertingNotify.State) (int64, error)
SaveNotificationLog(ctx context.Context, st alertingNotify.State) (int64, error)
SaveFlushLog(ctx context.Context, st alertingNotify.State) (int64, error)
GetSilences(ctx context.Context) (string, error)
GetNotificationLog(ctx context.Context) (string, error)
GetFlushLog(ctx context.Context) (string, error)
}
type alertmanager struct {
@@ -106,10 +101,6 @@ func NewAlertmanager(ctx context.Context, orgID int64, cfg *setting.Cfg, store A
if err != nil {
return nil, err
}
flushLog, err := stateStore.GetFlushLog(ctx)
if err != nil {
return nil, err
}
silencesOptions := maintenanceOptions{
initialState: silences,
@@ -132,29 +123,12 @@ func NewAlertmanager(ctx context.Context, orgID int64, cfg *setting.Cfg, store A
}
l := log.New("ngalert.notifier")
dispatchTimer := GetDispatchTimer(featureToggles)
var flushLogOptions *maintenanceOptions
if dispatchTimer == alertingNotify.DispatchTimerSync {
flushLogOptions = &maintenanceOptions{
initialState: flushLog,
retention: flushRetention,
maintenanceFrequency: maintenanceInterval,
maintenanceFunc: func(state alertingNotify.State) (int64, error) {
// Detached context here is to make sure that when the service is shut down the persist operation is executed.
return stateStore.SaveFlushLog(context.Background(), state)
},
}
}
opts := alertingNotify.GrafanaAlertmanagerOpts{
ExternalURL: cfg.AppURL,
AlertStoreCallback: nil,
PeerTimeout: cfg.UnifiedAlerting.HAPeerTimeout,
Silences: silencesOptions,
Nflog: nflogOptions,
FlushLog: flushLogOptions,
DispatchTimer: dispatchTimer,
Limits: alertingNotify.Limits{
MaxSilences: cfg.UnifiedAlerting.AlertmanagerMaxSilencesCount,
MaxSilenceSizeBytes: cfg.UnifiedAlerting.AlertmanagerMaxSilenceSizeBytes,
@@ -1,16 +0,0 @@
package notifier
import (
alertingNotify "github.com/grafana/alerting/notify"
"github.com/grafana/grafana/pkg/services/featuremgmt"
)
// GetDispatchTimer returns the appropriate dispatch timer based on feature toggles.
func GetDispatchTimer(features featuremgmt.FeatureToggles) (dt alertingNotify.DispatchTimer) {
//nolint:staticcheck // not yet migrated to OpenFeature
enabled := features.IsEnabledGlobally(featuremgmt.FlagAlertingSyncDispatchTimer)
if enabled {
dt = alertingNotify.DispatchTimerSync
}
return
}
@@ -1,36 +0,0 @@
package notifier
import (
"testing"
alertingNotify "github.com/grafana/alerting/notify"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/stretchr/testify/require"
)
func TestGetDispatchTimer(t *testing.T) {
tests := []struct {
name string
featureFlagValue bool
expected alertingNotify.DispatchTimer
}{
{
name: "feature flag enabled returns sync timer",
featureFlagValue: true,
expected: alertingNotify.DispatchTimerSync,
},
{
name: "feature flag disabled returns default timer",
featureFlagValue: false,
expected: alertingNotify.DispatchTimerDefault,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
features := featuremgmt.WithFeatures(featuremgmt.FlagAlertingSyncDispatchTimer, tt.featureFlagValue)
result := GetDispatchTimer(features)
require.Equal(t, tt.expected, result)
})
}
}
@@ -15,7 +15,6 @@ const (
KVNamespace = "alertmanager"
NotificationLogFilename = "notifications"
SilencesFilename = "silences"
FlushLogFilename = "flushes"
)
// FileStore is in charge of persisting the alertmanager files to the database.
@@ -43,10 +42,6 @@ func (fileStore *FileStore) GetNotificationLog(ctx context.Context) (string, err
return fileStore.contentFor(ctx, NotificationLogFilename)
}
func (fileStore *FileStore) GetFlushLog(ctx context.Context) (string, error) {
return fileStore.contentFor(ctx, FlushLogFilename)
}
// contentFor returns the content for the given Alertmanager kvstore key.
func (fileStore *FileStore) contentFor(ctx context.Context, filename string) (string, error) {
// Then, let's attempt to read it from the database.
@@ -79,11 +74,6 @@ func (fileStore *FileStore) SaveNotificationLog(ctx context.Context, st alerting
return fileStore.persist(ctx, NotificationLogFilename, st)
}
// SaveFlushLog saves the flush log to the database and returns the size of the unencoded state.
func (fileStore *FileStore) SaveFlushLog(ctx context.Context, st alertingNotify.State) (int64, error) {
return fileStore.persist(ctx, FlushLogFilename, st)
}
// persist takes care of persisting the binary representation of internal state to the database as a base64 encoded string.
func (fileStore *FileStore) persist(ctx context.Context, filename string, st alertingNotify.State) (int64, error) {
var size int64
@@ -106,48 +106,3 @@ func TestFileStore_NotificationLog(t *testing.T) {
t.Errorf("Unexpected Diff: %v", cmp.Diff(newState, decoded))
}
}
func TestFileStore_FlushLog(t *testing.T) {
store := fakes.NewFakeKVStore(t)
ctx := context.Background()
var orgId int64 = 1
// Initialize kvstore with empty flush log state.
initialState := flushLogState{} // FlushLog uses the same structure as nflog
decodedState, err := initialState.MarshalBinary()
require.NoError(t, err)
encodedState := base64.StdEncoding.EncodeToString(decodedState)
err = store.Set(ctx, orgId, KVNamespace, FlushLogFilename, encodedState)
require.NoError(t, err)
fs := NewFileStore(orgId, store)
// Load initial (empty).
flushLog, err := fs.GetFlushLog(ctx)
require.NoError(t, err)
decoded, err := decodeFlushLogState(strings.NewReader(flushLog))
require.NoError(t, err)
if !cmp.Equal(initialState, decoded) {
t.Errorf("Unexpected Diff: %v", cmp.Diff(initialState, decoded))
}
// Save new flush log state.
now := time.Now()
oneHour := now.Add(time.Hour)
v1 := createFlushLog(1, now, oneHour)
v2 := createFlushLog(2, now, oneHour)
newState := flushLogState{1: v1, 2: v2}
size, err := fs.SaveFlushLog(ctx, newState)
require.NoError(t, err)
require.Greater(t, size, int64(0))
// Load new.
flushLog, err = fs.GetFlushLog(ctx)
require.NoError(t, err)
decoded, err = decodeFlushLogState(strings.NewReader(flushLog))
require.NoError(t, err)
if !cmp.Equal(newState, decoded) {
t.Errorf("Unexpected Diff: %v", cmp.Diff(newState, decoded))
}
}
@@ -82,7 +82,6 @@ type Alertmanager interface {
type ExternalState struct {
Silences []byte
Nflog []byte
FlushLog []byte
}
// StateMerger describes a type that is able to merge external state (nflog, silences) with its own.
@@ -379,7 +378,7 @@ func (moa *MultiOrgAlertmanager) SyncAlertmanagersForOrgs(ctx context.Context, o
func (moa *MultiOrgAlertmanager) cleanupOrphanLocalOrgState(ctx context.Context,
activeOrganizations map[int64]struct{},
) {
storedFiles := []string{NotificationLogFilename, SilencesFilename, FlushLogFilename}
storedFiles := []string{NotificationLogFilename, SilencesFilename}
for _, fileName := range storedFiles {
keys, err := moa.kvStore.Keys(ctx, kvstore.AllOrganizations, KVNamespace, fileName)
if err != nil {
+1 -4
View File
@@ -5,8 +5,5 @@ func (am *alertmanager) MergeState(state ExternalState) error {
if err := am.Base.MergeNflog(state.Nflog); err != nil {
return err
}
if err := am.Base.MergeSilences(state.Silences); err != nil {
return err
}
return am.Base.MergeFlushLog(state.FlushLog)
return am.Base.MergeSilences(state.Silences)
}
+4 -48
View File
@@ -11,7 +11,6 @@ import (
"time"
"github.com/matttproud/golang_protobuf_extensions/pbutil"
"github.com/prometheus/alertmanager/flushlog/flushlogpb"
"github.com/prometheus/alertmanager/nflog/nflogpb"
"github.com/prometheus/alertmanager/silence/silencepb"
"github.com/prometheus/common/model"
@@ -229,13 +228,15 @@ func (f *FakeOrgStore) FetchOrgIds(_ context.Context) ([]int64, error) {
return f.orgs, nil
}
type NoValidation struct{}
type NoValidation struct {
}
func (n NoValidation) Validate(_ models.NotificationSettings) error {
return nil
}
type RejectingValidation struct{}
type RejectingValidation struct {
}
func (n RejectingValidation) Validate(s models.NotificationSettings) error {
return ErrorReceiverDoesNotExist{ErrorReferenceInvalid: ErrorReferenceInvalid{Reference: s.Receiver}}
@@ -364,51 +365,6 @@ func createNotificationLog(groupKey string, receiverName string, sentAt, expires
}
}
// https://github.com/grafana/prometheus-alertmanager/blob/main/flushlog/flushlog.go#L136-L136
type flushLogState map[uint64]*flushlogpb.MeshFlushLog
func (s flushLogState) MarshalBinary() ([]byte, error) {
var buf bytes.Buffer
for _, e := range s {
if _, err := pbutil.WriteDelimited(&buf, e); err != nil {
return nil, err
}
}
return buf.Bytes(), nil
}
func createFlushLog(groupFingerprint uint64, ts, expiresAt time.Time) *flushlogpb.MeshFlushLog {
return &flushlogpb.MeshFlushLog{
FlushLog: &flushlogpb.FlushLog{
GroupFingerprint: groupFingerprint,
Timestamp: ts,
},
ExpiresAt: expiresAt,
}
}
// decodeFlushLogState copied from decodeState in prometheus-alertmanager/flushlog/flushlog.go
func decodeFlushLogState(r io.Reader) (flushLogState, error) {
st := flushLogState{}
for {
var e flushlogpb.MeshFlushLog
_, err := pbutil.ReadDelimited(r, &e)
if err == nil {
if e.FlushLog == nil || e.FlushLog.GroupFingerprint == 0 || e.FlushLog.Timestamp.IsZero() {
return nil, errInvalidState
}
st[e.FlushLog.GroupFingerprint] = &e
continue
}
if errors.Is(err, io.EOF) {
break
}
return nil, err
}
return st, nil
}
type call struct {
Method string
Args []interface{}
+4 -20
View File
@@ -47,7 +47,6 @@ import (
type stateStore interface {
GetSilences(ctx context.Context) (string, error)
GetNotificationLog(ctx context.Context) (string, error)
GetFlushLog(ctx context.Context) (string, error)
}
// AutogenFn is a function that adds auto-generated routes to a configuration.
@@ -87,8 +86,6 @@ type Alertmanager struct {
promoteConfig bool
externalURL string
runtimeConfig remoteClient.RuntimeConfig
}
type AlertmanagerConfig struct {
@@ -114,9 +111,6 @@ type AlertmanagerConfig struct {
// Timeout for the HTTP client.
Timeout time.Duration
// RuntimeConfig specifies runtime behavior settings for the remote Alertmanager.
RuntimeConfig remoteClient.RuntimeConfig
}
func (cfg *AlertmanagerConfig) Validate() error {
@@ -209,7 +203,6 @@ func NewAlertmanager(ctx context.Context, cfg AlertmanagerConfig, store stateSto
externalURL: cfg.ExternalURL,
promoteConfig: cfg.PromoteConfig,
smtp: cfg.SmtpConfig,
runtimeConfig: cfg.RuntimeConfig,
}
// Parse the default configuration once and remember its hash so we can compare it later.
@@ -338,11 +331,10 @@ func (am *Alertmanager) buildConfiguration(ctx context.Context, raw []byte, crea
AlertmanagerConfig: mergeResult.Config,
Templates: templates,
},
CreatedAt: createdAtEpoch,
Promoted: am.promoteConfig,
ExternalURL: am.externalURL,
SmtpConfig: am.smtp,
RuntimeConfig: am.runtimeConfig,
CreatedAt: createdAtEpoch,
Promoted: am.promoteConfig,
ExternalURL: am.externalURL,
SmtpConfig: am.smtp,
}
cfgHash, err := calculateUserGrafanaConfigHash(payload)
@@ -396,8 +388,6 @@ func (am *Alertmanager) GetRemoteState(ctx context.Context) (notifier.ExternalSt
rs.Silences = p.Data
case "nfl":
rs.Nflog = p.Data
case "fls":
rs.FlushLog = p.Data
default:
return rs, fmt.Errorf("unknown part key %q", p.Key)
}
@@ -687,12 +677,6 @@ func (am *Alertmanager) getFullState(ctx context.Context) (string, error) {
}
parts = append(parts, alertingClusterPB.Part{Key: notifier.NotificationLogFilename, Data: []byte(notificationLog)})
flushLog, err := am.state.GetFlushLog(ctx)
if err != nil {
return "", fmt.Errorf("error getting flush log: %w", err)
}
parts = append(parts, alertingClusterPB.Part{Key: notifier.FlushLogFilename, Data: []byte(flushLog)})
fs := alertingClusterPB.FullState{
Parts: parts,
}
@@ -29,10 +29,6 @@ func (u *GrafanaAlertmanagerConfig) MarshalJSON() ([]byte, error) {
return definition.MarshalJSONWithSecrets((*cfg)(u))
}
type RuntimeConfig struct {
DispatchTimer string `json:"dispatch_timer"`
}
type UserGrafanaConfig struct {
GrafanaAlertmanagerConfig GrafanaAlertmanagerConfig `json:"configuration"`
Hash string `json:"configuration_hash"`
@@ -41,7 +37,6 @@ type UserGrafanaConfig struct {
Promoted bool `json:"promoted"`
ExternalURL string `json:"external_url"`
SmtpConfig SmtpConfig `json:"smtp_config"`
RuntimeConfig RuntimeConfig `json:"runtime_config"`
}
func (mc *Mimir) GetGrafanaAlertmanagerConfig(ctx context.Context) (*UserGrafanaConfig, error) {
-1
View File
@@ -600,7 +600,6 @@ type Cfg struct {
IndexRebuildInterval time.Duration
IndexCacheTTL time.Duration
IndexMinUpdateInterval time.Duration // Don't update index if it was updated less than this interval ago.
IndexScoringModel string // Note: Temporary config to switch the index scoring model and will be removed soon.
MaxFileIndexAge time.Duration // Max age of file-based indexes. Index older than this will be rebuilt asynchronously.
MinFileIndexBuildVersion string // Minimum version of Grafana that built the file-based index. If index was built with older Grafana, it will be rebuilt asynchronously.
EnableSharding bool
-4
View File
@@ -123,10 +123,6 @@ func (cfg *Cfg) setUnifiedStorageConfig() {
cfg.IndexRebuildInterval = section.Key("index_rebuild_interval").MustDuration(24 * time.Hour)
cfg.IndexCacheTTL = section.Key("index_cache_ttl").MustDuration(10 * time.Minute)
cfg.IndexMinUpdateInterval = section.Key("index_min_update_interval").MustDuration(0)
cfg.IndexScoringModel = section.Key("index_scoring_model").MustString("")
if cfg.IndexScoringModel != "" {
cfg.Logger.Info("Index scoring model set", "model", cfg.IndexScoringModel)
}
cfg.SprinklesApiServer = section.Key("sprinkles_api_server").String()
cfg.SprinklesApiServerPageLimit = section.Key("sprinkles_api_server_page_limit").MustInt(10000)
cfg.CACertPath = section.Key("ca_cert_path").String()
@@ -0,0 +1,35 @@
package dualwrite
import (
"context"
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
)
type ctxKey struct{}
type dualWriteContext struct {
updatedSecureValues common.InlineSecureValues
}
func addToContext(ctx context.Context) context.Context {
return context.WithValue(ctx, ctxKey{}, &dualWriteContext{})
}
// Get the Requester from context
func SetUpdatedSecureValues(ctx context.Context, sv common.InlineSecureValues) {
u, ok := ctx.Value(ctxKey{}).(*dualWriteContext)
if !ok || u == nil {
return // OK, this can happen when things are in mode 0 (legacy only)
}
u.updatedSecureValues = sv
}
// Get the Requester from context
func getUpdatedSecureValues(ctx context.Context) common.InlineSecureValues {
u, ok := ctx.Value(ctxKey{}).(*dualWriteContext)
if !ok || u == nil {
return nil
}
return u.updatedSecureValues
}
+41 -22
View File
@@ -16,14 +16,15 @@ import (
"k8s.io/apiserver/pkg/registry/rest"
"github.com/grafana/grafana-app-sdk/logging"
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
"github.com/grafana/grafana/pkg/apimachinery/utils"
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
)
var (
_ grafanarest.Storage = (*dualWriter)(nil)
tracer = otel.Tracer("github.com/grafana/grafana/pkg/storage/legacysql/dualwrite")
_ grafanarest.Storage = (*dualWriter)(nil)
tracer = otel.Tracer("github.com/grafana/grafana/pkg/storage/legacysql/dualwrite")
)
const (
@@ -203,7 +204,7 @@ func (d *dualWriter) Create(ctx context.Context, in runtime.Object, createValida
log := logging.FromContext(ctx).With("method", "Create")
accIn, err := meta.Accessor(in)
accIn, err := utils.MetaAccessor(in)
if err != nil {
return nil, err
}
@@ -216,19 +217,19 @@ func (d *dualWriter) Create(ctx context.Context, in runtime.Object, createValida
return nil, fmt.Errorf("name or generatename have to be set")
}
secure, err := accIn.GetSecureValues()
if err != nil {
return nil, fmt.Errorf("unable to read secure values %w", err)
}
readFromUnifiedWriteToBothStorages := d.readUnified && d.legacy != nil && d.unified != nil
permissions := ""
if readFromUnifiedWriteToBothStorages {
objIn, err := utils.MetaAccessor(in)
if err != nil {
return nil, err
}
// keep permissions, we will set it back after the object is created
permissions = objIn.GetAnnotation(utils.AnnoKeyGrantPermissions)
permissions = accIn.GetAnnotation(utils.AnnoKeyGrantPermissions)
if permissions != "" {
objIn.SetAnnotation(utils.AnnoKeyGrantPermissions, "") // remove the annotation for now
accIn.SetAnnotation(utils.AnnoKeyGrantPermissions, "") // remove the annotation for now
}
}
@@ -241,35 +242,36 @@ func (d *dualWriter) Create(ctx context.Context, in runtime.Object, createValida
}
createdCopy := createdFromLegacy.DeepCopyObject()
accCreated, err := meta.Accessor(createdCopy)
accCreated, err := utils.MetaAccessor(createdCopy)
if err != nil {
return nil, err
}
accCreated.SetResourceVersion("")
accCreated.SetUID("")
if secure != nil {
if err = accCreated.SetSecureValues(secure); err != nil {
return nil, fmt.Errorf("unable to set secure values on duplicate object %w", err)
}
}
if readFromUnifiedWriteToBothStorages {
objCopy, err := utils.MetaAccessor(createdCopy)
if err != nil {
return nil, err
}
// restore the permissions annotation, as we removed it before creating in legacy
if permissions != "" {
objCopy.SetAnnotation(utils.AnnoKeyGrantPermissions, permissions)
accCreated.SetAnnotation(utils.AnnoKeyGrantPermissions, permissions)
}
// Propagate annotations and labels to the object saved in
// unified storage, making sure the `deprecatedID` is saved
// as well as provisioning metadata, when present.
for name, val := range accIn.GetAnnotations() {
objCopy.SetAnnotation(name, val)
accCreated.SetAnnotation(name, val)
}
legacyAcc, err := meta.Accessor(createdFromLegacy)
if err != nil {
return nil, err
}
objCopy.SetLabels(legacyAcc.GetLabels())
accCreated.SetLabels(legacyAcc.GetLabels())
}
// If unified storage is the primary storage, let's just create it in the foreground and return it.
@@ -384,6 +386,7 @@ func (d *dualWriter) Update(ctx context.Context, name string, objInfo rest.Updat
// but legacy failed, the user would get a failure, but see the update did apply to the source
// of truth, and be less likely to retry to save (and get the stores in sync again)
ctx = addToContext(ctx)
legacyInfo := objInfo
legacyForceCreate := forceAllowCreate
unifiedInfo := objInfo
@@ -417,6 +420,14 @@ func (d *dualWriter) Update(ctx context.Context, name string, objInfo rest.Updat
}
}
// Propagate secure values from the update request to the unified storage update.
if secure := getUpdatedSecureValues(ctx); secure != nil {
wrapped, ok := unifiedInfo.(*wrappedUpdateInfo)
if ok {
wrapped.updatedSecureValues = secure
}
}
if d.readUnified {
return d.unified.Update(ctx, name, unifiedInfo, createValidation, updateValidation, unifiedForceCreate, options)
} else if d.errorIsOK {
@@ -515,9 +526,10 @@ func (d *dualWriter) ConvertToTable(ctx context.Context, object runtime.Object,
}
type wrappedUpdateInfo struct {
objInfo rest.UpdatedObjectInfo
legacyLabels map[string]string
legacyAnnotations map[string]string
objInfo rest.UpdatedObjectInfo
legacyLabels map[string]string
legacyAnnotations map[string]string
updatedSecureValues common.InlineSecureValues
}
// Preconditions implements rest.UpdatedObjectInfo.
@@ -560,6 +572,13 @@ func (w *wrappedUpdateInfo) UpdatedObject(ctx context.Context, oldObj runtime.Ob
meta.SetResourceVersion("")
meta.SetUID("")
if w.updatedSecureValues != nil {
if err = meta.SetSecureValues(w.updatedSecureValues); err != nil {
return nil, fmt.Errorf("unable to set secure values on duplicate object %w", err)
}
}
return obj, err
}
+55 -5
View File
@@ -3,8 +3,10 @@ package apistore
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"time"
"github.com/dustin/go-humanize"
@@ -83,6 +85,9 @@ func (s *Storage) prepareObjectForStorage(ctx context.Context, newObject runtime
if !ok {
return v, errors.New("missing auth info")
}
if err := s.checkGVK(newObject); err != nil {
return v, err
}
obj, err := utils.MetaAccessor(newObject)
if err != nil {
@@ -138,8 +143,7 @@ func (s *Storage) prepareObjectForStorage(ctx context.Context, newObject runtime
return v, err
}
err = s.codec.Encode(newObject, &v.raw)
if err == nil {
if err = s.encode(newObject, &v.raw); err == nil {
err = s.handleLargeResources(ctx, obj, &v.raw)
}
return v, err
@@ -152,6 +156,9 @@ func (s *Storage) prepareObjectForUpdate(ctx context.Context, updateObject runti
if !ok {
return v, errors.New("missing auth info")
}
if err := s.checkGVK(updateObject); err != nil {
return v, err
}
obj, err := utils.MetaAccessor(updateObject)
if err != nil {
@@ -233,8 +240,7 @@ func (s *Storage) prepareObjectForUpdate(ctx context.Context, updateObject runti
obj.SetAnnotation(utils.AnnoKeyUpdatedTimestamp, previous.GetAnnotation(utils.AnnoKeyUpdatedTimestamp))
}
err = s.codec.Encode(updateObject, &v.raw)
if err == nil {
if err = s.encode(updateObject, &v.raw); err == nil {
err = s.handleLargeResources(ctx, obj, &v.raw)
}
return v, err
@@ -268,7 +274,51 @@ func (s *Storage) handleLargeResources(ctx context.Context, obj utils.GrafanaMet
}
// Now encode the smaller version
return s.codec.Encode(orig, buf)
return s.encode(orig, buf)
}
return nil
}
func (s *Storage) checkGVK(obj runtime.Object) error {
if s.opts.Scheme == nil {
return nil // we can not do anything
}
// Ensure group+version+kind are configured
info := obj.GetObjectKind()
gvk := info.GroupVersionKind()
if gvk.Group == "" || gvk.Kind == "" || gvk.Version == "" {
gvks, _, err := s.opts.Scheme.ObjectKinds(obj)
if err != nil {
return fmt.Errorf("unknown object kind %w", err)
}
for _, v := range gvks {
if v.Group != s.gr.Group {
continue // skip values not in this group
}
gvk.Group = v.Group
gvk.Kind = v.Kind
if gvk.Version == "" {
gvk.Version = v.Version
}
info.SetGroupVersionKind(gvk)
return nil
}
}
return nil
}
func (s *Storage) encode(obj runtime.Object, w io.Writer) error {
// The standard encoder is fine when only one type maps to a group
if s.opts.Scheme == nil {
return s.codec.Encode(obj, w)
}
if err := s.checkGVK(obj); err != nil {
return err
}
// This will always write the saved GVK, unlike:
// https://github.com/kubernetes/kubernetes/blob/v1.34.3/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/versioning/versioning.go#L267
// that picks an arbitrary GVK that may not match the same group!
return json.NewEncoder(w).Encode(obj)
}
@@ -33,9 +33,11 @@ func TestPrepareObjectForStorage(t *testing.T) {
node, err := snowflake.NewNode(rand.Int64N(1024))
require.NoError(t, err)
s := &Storage{
gr: dashv1.DashboardResourceInfo.GroupResource(),
codec: apitesting.TestCodec(rtcodecs, dashv1.DashboardResourceInfo.GroupVersion()),
snowflake: node,
opts: StorageOptions{
Scheme: rtscheme,
EnableFolderSupport: true,
LargeObjectSupport: nil,
MaximumNameLength: 100,
+2
View File
@@ -57,6 +57,8 @@ type DefaultPermissionSetter = func(ctx context.Context, key *resourcepb.Resourc
// Optional settings that apply to a single resource
type StorageOptions struct {
Scheme *runtime.Scheme
// ????: should we constrain this to only dashboards for now?
// Not yet clear if this is a good general solution, or just a stop-gap
LargeObjectSupport LargeObjectSupport
-31
View File
@@ -9,13 +9,11 @@ import "resource.proto";
// Unlike the ResourceStore, this service can be exposed to clients directly
// It should be implemented with efficient indexes and does not need read-after-write semantics
service ResourceIndex {
// Query for documents
rpc Search(ResourceSearchRequest) returns (ResourceSearchResponse);
// Get the resource stats
rpc GetStats(ResourceStatsRequest) returns (ResourceStatsResponse);
// Rebuild the search index
rpc RebuildIndexes(RebuildIndexesRequest) returns (RebuildIndexesResponse);
}
@@ -51,20 +49,6 @@ message ResourceStatsResponse {
repeated Stats stats = 2;
}
// This controls what query and analyzers are applied to the specified field
// See: https://blevesearch.com/docs/Analyzers/
enum QueryFieldType {
// Picks a reasonable analyzer given the input. Currently this always uses TEXT
// In the future, it may change to depend on the indexed field type
DEFAULT = 0;
// Use free text analyzer. The query is broken into a normalized set of tokens
TEXT = 1;
// The query must exactly match the indexed token
KEYWORD = 2;
// Like a text query, but the position and offsets influence the score
PHRASE = 3;
}
// Search within a single resource
message ResourceSearchRequest {
message Sort {
@@ -80,18 +64,6 @@ message ResourceSearchRequest {
// date queries
}
// Defines the field in the index to query
// Boost is optional, and allows weighting the field higher in the results
message QueryField {
// The field name in the index to query
string name = 1;
QueryFieldType type = 2;
// Boost value for this field
float boost = 3;
}
// The key must include namespace + group + resource
ListOptions options = 1;
@@ -127,9 +99,6 @@ message ResourceSearchRequest {
int64 page = 11;
int64 permission = 12;
// Optionally specify which fields are included in the query
repeated QueryField query_fields = 13;
}
message ResourceSearchResponse {
+1
View File
@@ -290,6 +290,7 @@ const SEARCH_FIELD_NAMESPACE = "namespace"
const SEARCH_FIELD_NAME = "name"
const SEARCH_FIELD_RV = "rv"
const SEARCH_FIELD_TITLE = "title"
const SEARCH_FIELD_TITLE_NGRAM = "title_ngram"
const SEARCH_FIELD_TITLE_PHRASE = "title_phrase" // filtering/sorting on title by full phrase
const SEARCH_FIELD_DESCRIPTION = "description"
const SEARCH_FIELD_TAGS = "tags"
+139 -293
View File
@@ -21,65 +21,6 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// This controls what query and analyzers are applied to the specified field
// See: https://blevesearch.com/docs/Analyzers/
type QueryFieldType int32
const (
// Picks a reasonable analyzer given the input. Currently this always uses TEXT
// In the future, it may change to depend on the indexed field type
QueryFieldType_DEFAULT QueryFieldType = 0
// Use free text analyzer. The query is broken into a normalized set of tokens
QueryFieldType_TEXT QueryFieldType = 1
// The query must exactly match the indexed token
QueryFieldType_KEYWORD QueryFieldType = 2
// Like a text query, but the position and offsets influence the score
QueryFieldType_PHRASE QueryFieldType = 3
)
// Enum value maps for QueryFieldType.
var (
QueryFieldType_name = map[int32]string{
0: "DEFAULT",
1: "TEXT",
2: "KEYWORD",
3: "PHRASE",
}
QueryFieldType_value = map[string]int32{
"DEFAULT": 0,
"TEXT": 1,
"KEYWORD": 2,
"PHRASE": 3,
}
)
func (x QueryFieldType) Enum() *QueryFieldType {
p := new(QueryFieldType)
*p = x
return p
}
func (x QueryFieldType) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (QueryFieldType) Descriptor() protoreflect.EnumDescriptor {
return file_search_proto_enumTypes[0].Descriptor()
}
func (QueryFieldType) Type() protoreflect.EnumType {
return &file_search_proto_enumTypes[0]
}
func (x QueryFieldType) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use QueryFieldType.Descriptor instead.
func (QueryFieldType) EnumDescriptor() ([]byte, []int) {
return file_search_proto_rawDescGZIP(), []int{0}
}
// Get statistics across multiple resources
// For these queries, we do not need authorization to see the actual values
type ResourceStatsRequest struct {
@@ -224,12 +165,10 @@ type ResourceSearchRequest struct {
// the return fields (empty will return everything)
Fields []string `protobuf:"bytes,8,rep,name=fields,proto3" json:"fields,omitempty"`
// explain each result (added to the each row)
Explain bool `protobuf:"varint,9,opt,name=explain,proto3" json:"explain,omitempty"`
IsDeleted bool `protobuf:"varint,10,opt,name=is_deleted,json=isDeleted,proto3" json:"is_deleted,omitempty"`
Page int64 `protobuf:"varint,11,opt,name=page,proto3" json:"page,omitempty"`
Permission int64 `protobuf:"varint,12,opt,name=permission,proto3" json:"permission,omitempty"`
// Optionally specify which fields are included in the query
QueryFields []*ResourceSearchRequest_QueryField `protobuf:"bytes,13,rep,name=query_fields,json=queryFields,proto3" json:"query_fields,omitempty"`
Explain bool `protobuf:"varint,9,opt,name=explain,proto3" json:"explain,omitempty"`
IsDeleted bool `protobuf:"varint,10,opt,name=is_deleted,json=isDeleted,proto3" json:"is_deleted,omitempty"`
Page int64 `protobuf:"varint,11,opt,name=page,proto3" json:"page,omitempty"`
Permission int64 `protobuf:"varint,12,opt,name=permission,proto3" json:"permission,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@@ -348,13 +287,6 @@ func (x *ResourceSearchRequest) GetPermission() int64 {
return 0
}
func (x *ResourceSearchRequest) GetQueryFields() []*ResourceSearchRequest_QueryField {
if x != nil {
return x.QueryFields
}
return nil
}
type ResourceSearchResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
// Error details
@@ -738,70 +670,6 @@ func (x *ResourceSearchRequest_Facet) GetLimit() int64 {
return 0
}
// Defines the field in the index to query
// Boost is optional, and allows weighting the field higher in the results
type ResourceSearchRequest_QueryField struct {
state protoimpl.MessageState `protogen:"open.v1"`
// The field name in the index to query
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Type QueryFieldType `protobuf:"varint,2,opt,name=type,proto3,enum=resource.QueryFieldType" json:"type,omitempty"`
// Boost value for this field
Boost float32 `protobuf:"fixed32,3,opt,name=boost,proto3" json:"boost,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ResourceSearchRequest_QueryField) Reset() {
*x = ResourceSearchRequest_QueryField{}
mi := &file_search_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ResourceSearchRequest_QueryField) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ResourceSearchRequest_QueryField) ProtoMessage() {}
func (x *ResourceSearchRequest_QueryField) ProtoReflect() protoreflect.Message {
mi := &file_search_proto_msgTypes[9]
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 ResourceSearchRequest_QueryField.ProtoReflect.Descriptor instead.
func (*ResourceSearchRequest_QueryField) Descriptor() ([]byte, []int) {
return file_search_proto_rawDescGZIP(), []int{2, 2}
}
func (x *ResourceSearchRequest_QueryField) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *ResourceSearchRequest_QueryField) GetType() QueryFieldType {
if x != nil {
return x.Type
}
return QueryFieldType_DEFAULT
}
func (x *ResourceSearchRequest_QueryField) GetBoost() float32 {
if x != nil {
return x.Boost
}
return 0
}
type ResourceSearchResponse_Facet struct {
state protoimpl.MessageState `protogen:"open.v1"`
Field string `protobuf:"bytes,1,opt,name=field,proto3" json:"field,omitempty"`
@@ -817,7 +685,7 @@ type ResourceSearchResponse_Facet struct {
func (x *ResourceSearchResponse_Facet) Reset() {
*x = ResourceSearchResponse_Facet{}
mi := &file_search_proto_msgTypes[11]
mi := &file_search_proto_msgTypes[10]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -829,7 +697,7 @@ func (x *ResourceSearchResponse_Facet) String() string {
func (*ResourceSearchResponse_Facet) ProtoMessage() {}
func (x *ResourceSearchResponse_Facet) ProtoReflect() protoreflect.Message {
mi := &file_search_proto_msgTypes[11]
mi := &file_search_proto_msgTypes[10]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -883,7 +751,7 @@ type ResourceSearchResponse_TermFacet struct {
func (x *ResourceSearchResponse_TermFacet) Reset() {
*x = ResourceSearchResponse_TermFacet{}
mi := &file_search_proto_msgTypes[12]
mi := &file_search_proto_msgTypes[11]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -895,7 +763,7 @@ func (x *ResourceSearchResponse_TermFacet) String() string {
func (*ResourceSearchResponse_TermFacet) ProtoMessage() {}
func (x *ResourceSearchResponse_TermFacet) ProtoReflect() protoreflect.Message {
mi := &file_search_proto_msgTypes[12]
mi := &file_search_proto_msgTypes[11]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -950,7 +818,7 @@ var file_search_proto_rawDesc = string([]byte{
0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63,
0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e,
0x74, 0x22, 0xc3, 0x06, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65,
0x74, 0x22, 0x8e, 0x05, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65,
0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x07, 0x6f,
0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72,
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x70, 0x74, 0x69,
@@ -978,109 +846,93 @@ var file_search_proto_rawDesc = string([]byte{
0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x0b,
0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x65,
0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a,
0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x4d, 0x0a, 0x0c, 0x71, 0x75,
0x65, 0x72, 0x79, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x2a, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f,
0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0x30, 0x0a, 0x04, 0x53, 0x6f,
0x72, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x63,
0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x64, 0x65, 0x73, 0x63, 0x1a, 0x33, 0x0a, 0x05,
0x46, 0x61, 0x63, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c,
0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69,
0x74, 0x1a, 0x5f, 0x0a, 0x0a, 0x46, 0x61, 0x63, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12,
0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65,
0x79, 0x12, 0x3b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x25, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f,
0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0b, 0x71, 0x75,
0x65, 0x72, 0x79, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x1a, 0x30, 0x0a, 0x04, 0x53, 0x6f, 0x72,
0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x63, 0x18,
0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x64, 0x65, 0x73, 0x63, 0x1a, 0x33, 0x0a, 0x05, 0x46,
0x61, 0x63, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69,
0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74,
0x1a, 0x64, 0x0a, 0x0a, 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x12,
0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
0x6d, 0x65, 0x12, 0x2c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e,
0x32, 0x18, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x51, 0x75, 0x65, 0x72,
0x79, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65,
0x12, 0x14, 0x0a, 0x05, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x52,
0x05, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x1a, 0x5f, 0x0a, 0x0a, 0x46, 0x61, 0x63, 0x65, 0x74, 0x45,
0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x3b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x61, 0x63, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61,
0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xea, 0x04, 0x0a, 0x16, 0x52, 0x65, 0x73, 0x6f,
0x74, 0x2e, 0x46, 0x61, 0x63, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
0x38, 0x01, 0x22, 0xea, 0x04, 0x0a, 0x16, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53,
0x65, 0x61, 0x72, 0x63, 0x68, 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, 0x27, 0x0a, 0x03, 0x6b, 0x65,
0x79, 0x18, 0x02, 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, 0x12, 0x31, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x03,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e,
0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x07, 0x72,
0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f,
0x68, 0x69, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x6f, 0x74, 0x61,
0x6c, 0x48, 0x69, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x63,
0x6f, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09, 0x71, 0x75, 0x65, 0x72, 0x79,
0x43, 0x6f, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x63, 0x6f, 0x72,
0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x53, 0x63, 0x6f, 0x72,
0x65, 0x12, 0x41, 0x0a, 0x05, 0x66, 0x61, 0x63, 0x65, 0x74, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x2b, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f,
0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 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,
0x27, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72,
0x73, 0x65, 0x2e, 0x46, 0x61, 0x63, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x66,
0x61, 0x63, 0x65, 0x74, 0x1a, 0x8f, 0x01, 0x0a, 0x05, 0x46, 0x61, 0x63, 0x65, 0x74, 0x12, 0x14,
0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66,
0x69, 0x65, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x02, 0x20,
0x01, 0x28, 0x03, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x69,
0x73, 0x73, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6d, 0x69, 0x73,
0x73, 0x69, 0x6e, 0x67, 0x12, 0x40, 0x0a, 0x05, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x18, 0x04, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52,
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x54, 0x65, 0x72, 0x6d, 0x46, 0x61, 0x63, 0x65, 0x74, 0x52,
0x05, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x1a, 0x35, 0x0a, 0x09, 0x54, 0x65, 0x72, 0x6d, 0x46, 0x61,
0x63, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x72, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x04, 0x74, 0x65, 0x72, 0x6d, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74,
0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x1a, 0x60, 0x0a,
0x0a, 0x46, 0x61, 0x63, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b,
0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x3c, 0x0a,
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 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, 0x12, 0x31, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75,
0x6c, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x72, 0x65, 0x73, 0x6f,
0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x61, 0x62,
0x6c, 0x65, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74,
0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x68, 0x69, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52,
0x09, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x48, 0x69, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x71, 0x75,
0x65, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09,
0x71, 0x75, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78,
0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x6d, 0x61,
0x78, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x41, 0x0a, 0x05, 0x66, 0x61, 0x63, 0x65, 0x74, 0x18,
0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x46, 0x61, 0x63, 0x65, 0x74, 0x45, 0x6e, 0x74,
0x72, 0x79, 0x52, 0x05, 0x66, 0x61, 0x63, 0x65, 0x74, 0x1a, 0x8f, 0x01, 0x0a, 0x05, 0x46, 0x61,
0x63, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74,
0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12,
0x18, 0x0a, 0x07, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03,
0x52, 0x07, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x40, 0x0a, 0x05, 0x74, 0x65, 0x72,
0x6d, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75,
0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72,
0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x54, 0x65, 0x72, 0x6d, 0x46,
0x61, 0x63, 0x65, 0x74, 0x52, 0x05, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x1a, 0x35, 0x0a, 0x09, 0x54,
0x65, 0x72, 0x6d, 0x46, 0x61, 0x63, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x72, 0x6d,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x72, 0x6d, 0x12, 0x14, 0x0a, 0x05,
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75,
0x6e, 0x74, 0x1a, 0x60, 0x0a, 0x0a, 0x46, 0x61, 0x63, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79,
0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
0x65, 0x79, 0x12, 0x3c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x26, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73,
0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x2e, 0x46, 0x61, 0x63, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x3a, 0x02, 0x38, 0x01, 0x22, 0x60, 0x0a, 0x15, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49,
0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a,
0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x29, 0x0a, 0x04, 0x6b,
0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x03, 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, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x22, 0x83, 0x01, 0x0a, 0x16, 0x52, 0x65, 0x62, 0x75, 0x69,
0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x22, 0x0a, 0x0c, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x75, 0x6e,
0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64,
0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12,
0x2b, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 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, 0x2a, 0x40, 0x0a, 0x0e,
0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b,
0x0a, 0x07, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x54,
0x45, 0x58, 0x54, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x4b, 0x45, 0x59, 0x57, 0x4f, 0x52, 0x44,
0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x48, 0x52, 0x41, 0x53, 0x45, 0x10, 0x03, 0x32, 0xfe,
0x01, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78,
0x12, 0x4b, 0x0a, 0x06, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x1f, 0x2e, 0x72, 0x65, 0x73,
0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65,
0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x72, 0x65,
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53,
0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a,
0x08, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x1e, 0x2e, 0x72, 0x65, 0x73, 0x6f,
0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61,
0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x72, 0x65, 0x73, 0x6f,
0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61,
0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x0e, 0x52, 0x65,
0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x72,
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49,
0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e,
0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64,
0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 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,
0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x46,
0x61, 0x63, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22,
0x60, 0x0a, 0x15, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65,
0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65,
0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d,
0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x29, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02,
0x20, 0x03, 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, 0x04, 0x6b, 0x65, 0x79,
0x73, 0x22, 0x83, 0x01, 0x0a, 0x16, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64,
0x65, 0x78, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x22, 0x0a, 0x0c,
0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01,
0x28, 0x03, 0x52, 0x0c, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74,
0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x2b, 0x0a, 0x05, 0x65, 0x72,
0x72, 0x6f, 0x72, 0x18, 0x03, 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, 0x32, 0xfe, 0x01, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x6f,
0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x4b, 0x0a, 0x06, 0x53, 0x65, 0x61,
0x72, 0x63, 0x68, 0x12, 0x1f, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52,
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e,
0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61,
0x74, 0x73, 0x12, 0x1e, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65,
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65,
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x0e, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e,
0x64, 0x65, 0x78, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
0x2e, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
0x65, 0x2e, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73,
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 (
@@ -1095,58 +947,53 @@ func file_search_proto_rawDescGZIP() []byte {
return file_search_proto_rawDescData
}
var file_search_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_search_proto_msgTypes = make([]protoimpl.MessageInfo, 14)
var file_search_proto_msgTypes = make([]protoimpl.MessageInfo, 13)
var file_search_proto_goTypes = []any{
(QueryFieldType)(0), // 0: resource.QueryFieldType
(*ResourceStatsRequest)(nil), // 1: resource.ResourceStatsRequest
(*ResourceStatsResponse)(nil), // 2: resource.ResourceStatsResponse
(*ResourceSearchRequest)(nil), // 3: resource.ResourceSearchRequest
(*ResourceSearchResponse)(nil), // 4: resource.ResourceSearchResponse
(*RebuildIndexesRequest)(nil), // 5: resource.RebuildIndexesRequest
(*RebuildIndexesResponse)(nil), // 6: resource.RebuildIndexesResponse
(*ResourceStatsResponse_Stats)(nil), // 7: resource.ResourceStatsResponse.Stats
(*ResourceSearchRequest_Sort)(nil), // 8: resource.ResourceSearchRequest.Sort
(*ResourceSearchRequest_Facet)(nil), // 9: resource.ResourceSearchRequest.Facet
(*ResourceSearchRequest_QueryField)(nil), // 10: resource.ResourceSearchRequest.QueryField
nil, // 11: resource.ResourceSearchRequest.FacetEntry
(*ResourceSearchResponse_Facet)(nil), // 12: resource.ResourceSearchResponse.Facet
(*ResourceSearchResponse_TermFacet)(nil), // 13: resource.ResourceSearchResponse.TermFacet
nil, // 14: resource.ResourceSearchResponse.FacetEntry
(*ErrorResult)(nil), // 15: resource.ErrorResult
(*ListOptions)(nil), // 16: resource.ListOptions
(*ResourceKey)(nil), // 17: resource.ResourceKey
(*ResourceTable)(nil), // 18: resource.ResourceTable
(*ResourceStatsRequest)(nil), // 0: resource.ResourceStatsRequest
(*ResourceStatsResponse)(nil), // 1: resource.ResourceStatsResponse
(*ResourceSearchRequest)(nil), // 2: resource.ResourceSearchRequest
(*ResourceSearchResponse)(nil), // 3: resource.ResourceSearchResponse
(*RebuildIndexesRequest)(nil), // 4: resource.RebuildIndexesRequest
(*RebuildIndexesResponse)(nil), // 5: resource.RebuildIndexesResponse
(*ResourceStatsResponse_Stats)(nil), // 6: resource.ResourceStatsResponse.Stats
(*ResourceSearchRequest_Sort)(nil), // 7: resource.ResourceSearchRequest.Sort
(*ResourceSearchRequest_Facet)(nil), // 8: resource.ResourceSearchRequest.Facet
nil, // 9: resource.ResourceSearchRequest.FacetEntry
(*ResourceSearchResponse_Facet)(nil), // 10: resource.ResourceSearchResponse.Facet
(*ResourceSearchResponse_TermFacet)(nil), // 11: resource.ResourceSearchResponse.TermFacet
nil, // 12: resource.ResourceSearchResponse.FacetEntry
(*ErrorResult)(nil), // 13: resource.ErrorResult
(*ListOptions)(nil), // 14: resource.ListOptions
(*ResourceKey)(nil), // 15: resource.ResourceKey
(*ResourceTable)(nil), // 16: resource.ResourceTable
}
var file_search_proto_depIdxs = []int32{
15, // 0: resource.ResourceStatsResponse.error:type_name -> resource.ErrorResult
7, // 1: resource.ResourceStatsResponse.stats:type_name -> resource.ResourceStatsResponse.Stats
16, // 2: resource.ResourceSearchRequest.options:type_name -> resource.ListOptions
17, // 3: resource.ResourceSearchRequest.federated:type_name -> resource.ResourceKey
8, // 4: resource.ResourceSearchRequest.sortBy:type_name -> resource.ResourceSearchRequest.Sort
11, // 5: resource.ResourceSearchRequest.facet:type_name -> resource.ResourceSearchRequest.FacetEntry
10, // 6: resource.ResourceSearchRequest.query_fields:type_name -> resource.ResourceSearchRequest.QueryField
15, // 7: resource.ResourceSearchResponse.error:type_name -> resource.ErrorResult
17, // 8: resource.ResourceSearchResponse.key:type_name -> resource.ResourceKey
18, // 9: resource.ResourceSearchResponse.results:type_name -> resource.ResourceTable
14, // 10: resource.ResourceSearchResponse.facet:type_name -> resource.ResourceSearchResponse.FacetEntry
17, // 11: resource.RebuildIndexesRequest.keys:type_name -> resource.ResourceKey
15, // 12: resource.RebuildIndexesResponse.error:type_name -> resource.ErrorResult
0, // 13: resource.ResourceSearchRequest.QueryField.type:type_name -> resource.QueryFieldType
9, // 14: resource.ResourceSearchRequest.FacetEntry.value:type_name -> resource.ResourceSearchRequest.Facet
13, // 15: resource.ResourceSearchResponse.Facet.terms:type_name -> resource.ResourceSearchResponse.TermFacet
12, // 16: resource.ResourceSearchResponse.FacetEntry.value:type_name -> resource.ResourceSearchResponse.Facet
3, // 17: resource.ResourceIndex.Search:input_type -> resource.ResourceSearchRequest
1, // 18: resource.ResourceIndex.GetStats:input_type -> resource.ResourceStatsRequest
5, // 19: resource.ResourceIndex.RebuildIndexes:input_type -> resource.RebuildIndexesRequest
4, // 20: resource.ResourceIndex.Search:output_type -> resource.ResourceSearchResponse
2, // 21: resource.ResourceIndex.GetStats:output_type -> resource.ResourceStatsResponse
6, // 22: resource.ResourceIndex.RebuildIndexes:output_type -> resource.RebuildIndexesResponse
20, // [20:23] is the sub-list for method output_type
17, // [17:20] is the sub-list for method input_type
17, // [17:17] is the sub-list for extension type_name
17, // [17:17] is the sub-list for extension extendee
0, // [0:17] is the sub-list for field type_name
13, // 0: resource.ResourceStatsResponse.error:type_name -> resource.ErrorResult
6, // 1: resource.ResourceStatsResponse.stats:type_name -> resource.ResourceStatsResponse.Stats
14, // 2: resource.ResourceSearchRequest.options:type_name -> resource.ListOptions
15, // 3: resource.ResourceSearchRequest.federated:type_name -> resource.ResourceKey
7, // 4: resource.ResourceSearchRequest.sortBy:type_name -> resource.ResourceSearchRequest.Sort
9, // 5: resource.ResourceSearchRequest.facet:type_name -> resource.ResourceSearchRequest.FacetEntry
13, // 6: resource.ResourceSearchResponse.error:type_name -> resource.ErrorResult
15, // 7: resource.ResourceSearchResponse.key:type_name -> resource.ResourceKey
16, // 8: resource.ResourceSearchResponse.results:type_name -> resource.ResourceTable
12, // 9: resource.ResourceSearchResponse.facet:type_name -> resource.ResourceSearchResponse.FacetEntry
15, // 10: resource.RebuildIndexesRequest.keys:type_name -> resource.ResourceKey
13, // 11: resource.RebuildIndexesResponse.error:type_name -> resource.ErrorResult
8, // 12: resource.ResourceSearchRequest.FacetEntry.value:type_name -> resource.ResourceSearchRequest.Facet
11, // 13: resource.ResourceSearchResponse.Facet.terms:type_name -> resource.ResourceSearchResponse.TermFacet
10, // 14: resource.ResourceSearchResponse.FacetEntry.value:type_name -> resource.ResourceSearchResponse.Facet
2, // 15: resource.ResourceIndex.Search:input_type -> resource.ResourceSearchRequest
0, // 16: resource.ResourceIndex.GetStats:input_type -> resource.ResourceStatsRequest
4, // 17: resource.ResourceIndex.RebuildIndexes:input_type -> resource.RebuildIndexesRequest
3, // 18: resource.ResourceIndex.Search:output_type -> resource.ResourceSearchResponse
1, // 19: resource.ResourceIndex.GetStats:output_type -> resource.ResourceStatsResponse
5, // 20: resource.ResourceIndex.RebuildIndexes:output_type -> resource.RebuildIndexesResponse
18, // [18:21] is the sub-list for method output_type
15, // [15:18] is the sub-list for method input_type
15, // [15:15] is the sub-list for extension type_name
15, // [15:15] is the sub-list for extension extendee
0, // [0:15] is the sub-list for field type_name
}
func init() { file_search_proto_init() }
@@ -1160,14 +1007,13 @@ func file_search_proto_init() {
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_search_proto_rawDesc), len(file_search_proto_rawDesc)),
NumEnums: 1,
NumMessages: 14,
NumEnums: 0,
NumMessages: 13,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_search_proto_goTypes,
DependencyIndexes: file_search_proto_depIdxs,
EnumInfos: file_search_proto_enumTypes,
MessageInfos: file_search_proto_msgTypes,
}.Build()
File_search_proto = out.File
@@ -31,11 +31,9 @@ const (
// Unlike the ResourceStore, this service can be exposed to clients directly
// It should be implemented with efficient indexes and does not need read-after-write semantics
type ResourceIndexClient interface {
// Query for documents
Search(ctx context.Context, in *ResourceSearchRequest, opts ...grpc.CallOption) (*ResourceSearchResponse, error)
// Get the resource stats
GetStats(ctx context.Context, in *ResourceStatsRequest, opts ...grpc.CallOption) (*ResourceStatsResponse, error)
// Rebuild the search index
RebuildIndexes(ctx context.Context, in *RebuildIndexesRequest, opts ...grpc.CallOption) (*RebuildIndexesResponse, error)
}
@@ -84,11 +82,9 @@ func (c *resourceIndexClient) RebuildIndexes(ctx context.Context, in *RebuildInd
// Unlike the ResourceStore, this service can be exposed to clients directly
// It should be implemented with efficient indexes and does not need read-after-write semantics
type ResourceIndexServer interface {
// Query for documents
Search(context.Context, *ResourceSearchRequest) (*ResourceSearchResponse, error)
// Get the resource stats
GetStats(context.Context, *ResourceStatsRequest) (*ResourceStatsResponse, error)
// Rebuild the search index
RebuildIndexes(context.Context, *RebuildIndexesRequest) (*RebuildIndexesResponse, error)
}
+34 -60
View File
@@ -81,11 +81,6 @@ type BleveOptions struct {
// Indexes that are not owned by current instance are eligible for cleanup.
// If nil, all indexes are owned by the current instance.
OwnsIndex func(key resource.NamespacedResource) (bool, error)
// ScoringModel defines the scoring model used for the bleve indexes
// Default: index.TFIDFScoring
// Supported values: index.TFIDFScoring and index.BM25Scoring
ScoringModel string
}
type bleveBackend struct {
@@ -373,7 +368,7 @@ func (b *bleveBackend) BuildIndex(
attribute.String("reason", indexBuildReason),
)
mapper, err := GetBleveMappings(b.opts.ScoringModel, fields)
mapper, err := GetBleveMappings(fields)
if err != nil {
return nil, err
}
@@ -1182,7 +1177,6 @@ func (b *bleveIndex) getIndex(
return b.index, nil
}
// nolint:gocyclo
func (b *bleveIndex) toBleveSearchRequest(ctx context.Context, req *resourcepb.ResourceSearchRequest, access authlib.AccessClient) (*bleve.SearchRequest, *resourcepb.ErrorResult) {
ctx, span := tracer.Start(ctx, "search.bleveIndex.toBleveSearchRequest")
defer span.End()
@@ -1241,62 +1235,42 @@ func (b *bleveIndex) toBleveSearchRequest(ctx context.Context, req *resourcepb.R
}
}
if len(req.Query) > 1 {
if strings.Contains(req.Query, "*") {
// wildcard query is expensive - should be used with caution
wildcard := bleve.NewWildcardQuery(req.Query)
queries = append(queries, wildcard)
} else {
// When using a
searchrequest.Fields = append(searchrequest.Fields, resource.SEARCH_FIELD_SCORE)
disjoin := bleve.NewDisjunctionQuery()
queries = append(queries, disjoin)
if len(req.Query) > 1 && strings.Contains(req.Query, "*") {
// wildcard query is expensive - should be used with caution
wildcard := bleve.NewWildcardQuery(req.Query)
queries = append(queries, wildcard)
}
queryFields := req.QueryFields
if len(queryFields) == 0 {
queryFields = []*resourcepb.ResourceSearchRequest_QueryField{
{
Name: resource.SEARCH_FIELD_TITLE,
Type: resourcepb.QueryFieldType_KEYWORD,
Boost: 10, // exact match -- includes ngrams! If they lived on their own field, we could score them differently
}, {
Name: resource.SEARCH_FIELD_TITLE,
Type: resourcepb.QueryFieldType_TEXT,
Boost: 2, // standard analyzer (with ngrams!)
}, {
Name: resource.SEARCH_FIELD_TITLE_PHRASE,
Type: resourcepb.QueryFieldType_TEXT,
Boost: 5, // standard analyzer
},
}
}
if req.Query != "" && !strings.Contains(req.Query, "*") {
// Add a text query
searchrequest.Fields = append(searchrequest.Fields, resource.SEARCH_FIELD_SCORE)
for _, field := range queryFields {
switch field.Type {
case resourcepb.QueryFieldType_TEXT, resourcepb.QueryFieldType_DEFAULT:
q := bleve.NewMatchQuery(removeSmallTerms(req.Query)) // removeSmallTerms should be part of the analyzer
q.SetBoost(float64(field.Boost))
q.SetField(field.Name)
q.Analyzer = standard.Name // analyze the text
q.Operator = query.MatchQueryOperatorAnd // all terms must match
disjoin.AddQuery(q)
// There are multiple ways to match the query string to documents. The following queries are ordered by priority:
case resourcepb.QueryFieldType_KEYWORD:
q := bleve.NewMatchQuery(req.Query)
q.SetBoost(float64(field.Boost))
q.SetField(field.Name)
q.Analyzer = keyword.Name // don't analyze the query input - treat it as a single token
disjoin.AddQuery(q)
// Query 1: Match the exact query string
queryExact := bleve.NewMatchQuery(req.Query)
queryExact.SetBoost(10.0)
queryExact.SetField(resource.SEARCH_FIELD_TITLE)
queryExact.Analyzer = keyword.Name // don't analyze the query input - treat it as a single token
queryExact.Operator = query.MatchQueryOperatorAnd // This doesn't make a difference for keyword analyzer, we add it just to be explicit.
searchQuery := bleve.NewDisjunctionQuery(queryExact)
case resourcepb.QueryFieldType_PHRASE:
q := bleve.NewMatchPhraseQuery(req.Query)
q.SetBoost(float64(field.Boost))
q.SetField(field.Name)
q.Analyzer = standard.Name
disjoin.AddQuery(q)
}
}
}
// Query 2: Phrase query with standard analyzer
queryPhrase := bleve.NewMatchPhraseQuery(req.Query)
queryPhrase.SetBoost(5.0)
queryPhrase.SetField(resource.SEARCH_FIELD_TITLE)
queryPhrase.Analyzer = standard.Name
searchQuery.AddQuery(queryPhrase)
// Query 3: Match query with standard analyzer
queryAnalyzed := bleve.NewMatchQuery(removeSmallTerms(req.Query))
queryAnalyzed.SetField(resource.SEARCH_FIELD_TITLE)
queryAnalyzed.SetBoost(2.0)
queryAnalyzed.Analyzer = standard.Name
queryAnalyzed.Operator = query.MatchQueryOperatorAnd // Make sure all terms from the query are matched
searchQuery.AddQuery(queryAnalyzed)
queries = append(queries, searchQuery)
}
switch len(queries) {
@@ -1898,7 +1872,7 @@ func (q *permissionScopedQuery) Searcher(ctx context.Context, i index.IndexReade
if err != nil {
return nil, err
}
filteringSearcher := bleveSearch.NewFilteringSearcher(ctx, searcher, func(_ *search.SearchContext, d *search.DocumentMatch) bool {
filteringSearcher := bleveSearch.NewFilteringSearcher(ctx, searcher, func(d *search.DocumentMatch) bool {
// The doc ID has the format: <namespace>/<group>/<resourceType>/<name>
// IndexInternalID will be the same as the doc ID when using an in-memory index, but when using a file-based
// index it becomes a binary encoded number that has some other internal meaning. Using ExternalID() will get the
@@ -4,7 +4,6 @@ import (
"context"
"testing"
index "github.com/blevesearch/bleve_index_api"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/storage/unified/resource"
@@ -20,7 +19,6 @@ func TestBleveSearchBackend(t *testing.T) {
backend, err := NewBleveBackend(BleveOptions{
Root: tempDir,
FileThreshold: 5,
ScoringModel: index.BM25Scoring,
}, nil)
require.NoError(t, err)
require.NotNil(t, backend)
@@ -54,32 +52,3 @@ func TestSearchBackendBenchmark(t *testing.T) {
unitest.BenchmarkSearchBackend(t, backend, opts)
}
func BenchmarkScoringModels(b *testing.B) {
models := []string{index.TFIDFScoring, index.BM25Scoring}
for _, model := range models {
b.Run(model, func(b *testing.B) {
tempDir := b.TempDir()
backend, err := NewBleveBackend(BleveOptions{
Root: tempDir,
ScoringModel: model,
}, nil)
require.NoError(b, err)
require.NotNil(b, backend)
b.Cleanup(backend.Stop)
opts := &unitest.BenchmarkOptions{
NumResources: 1000,
Concurrency: 4,
NumNamespaces: 10,
NumGroups: 10,
NumResourceTypes: 10,
}
unitest.BenchmarkSearchBackend(b, backend, opts)
})
}
}
+2 -4
View File
@@ -5,15 +5,13 @@ import (
"github.com/blevesearch/bleve/v2/analysis/analyzer/keyword"
"github.com/blevesearch/bleve/v2/analysis/analyzer/standard"
"github.com/blevesearch/bleve/v2/mapping"
"github.com/grafana/grafana/pkg/storage/unified/resource"
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
)
func GetBleveMappings(scoringModel string, fields resource.SearchableDocumentFields) (mapping.IndexMapping, error) {
func GetBleveMappings(fields resource.SearchableDocumentFields) (mapping.IndexMapping, error) {
mapper := bleve.NewIndexMapping()
if scoringModel != "" {
mapper.ScoringModel = scoringModel
}
err := RegisterCustomAnalyzers(mapper)
if err != nil {
@@ -13,7 +13,7 @@ import (
)
func TestDocumentMapping(t *testing.T) {
mappings, err := search.GetBleveMappings("", nil)
mappings, err := search.GetBleveMappings(nil)
require.NoError(t, err)
data := resource.IndexableDocument{
Title: "title",
@@ -7,7 +7,6 @@ import (
"testing"
"github.com/blevesearch/bleve/v2"
index "github.com/blevesearch/bleve_index_api"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/apimachinery/identity"
@@ -259,7 +258,6 @@ func newTestDashboardsIndex(t testing.TB, threshold int64, size int64, writer re
backend, err := search.NewBleveBackend(search.BleveOptions{
Root: t.TempDir(),
FileThreshold: threshold, // use in-memory for tests
ScoringModel: index.BM25Scoring,
}, nil)
require.NoError(t, err)
-3
View File
@@ -14,7 +14,6 @@ import (
"time"
"github.com/blevesearch/bleve/v2"
index "github.com/blevesearch/bleve_index_api"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/testutil"
"github.com/stretchr/testify/assert"
@@ -51,7 +50,6 @@ func TestBleveBackend(t *testing.T) {
backend, err := NewBleveBackend(BleveOptions{
Root: tmpdir,
FileThreshold: 5, // with more than 5 items we create a file on disk
ScoringModel: index.BM25Scoring,
}, nil)
require.NoError(t, err)
t.Cleanup(backend.Stop)
@@ -775,7 +773,6 @@ func setupBleveBackend(t *testing.T, options ...setupOption) (*bleveBackend, pro
IndexCacheTTL: defaultIndexCacheTTL,
Logger: log.NewNopLogger(),
BuildVersion: buildVersion,
ScoringModel: index.BM25Scoring,
}
for _, opt := range options {
opt(&opts)
-1
View File
@@ -46,7 +46,6 @@ func NewSearchOptions(
BuildVersion: cfg.BuildVersion,
OwnsIndex: ownsIndexFn,
IndexMinUpdateInterval: cfg.IndexMinUpdateInterval,
ScoringModel: cfg.IndexScoringModel,
}, indexMetrics)
if err != nil {
@@ -6,7 +6,6 @@ import (
"testing"
"time"
index "github.com/blevesearch/bleve_index_api"
"github.com/go-jose/go-jose/v4/jwt"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/require"
@@ -130,28 +129,21 @@ func TestIntegrationSearchAndStorage(t *testing.T) {
ctx := context.Background()
scoringModels := []string{index.TFIDFScoring, index.BM25Scoring}
// Create a new bleve backend
search, err := search.NewBleveBackend(search.BleveOptions{
FileThreshold: 0,
Root: t.TempDir(),
}, nil)
require.NoError(t, err)
require.NotNil(t, search)
t.Cleanup(search.Stop)
for _, model := range scoringModels {
t.Run(model, func(t *testing.T) {
// Create a new bleve backend
search, err := search.NewBleveBackend(search.BleveOptions{
FileThreshold: 0,
Root: t.TempDir(),
ScoringModel: model,
}, nil)
require.NoError(t, err)
require.NotNil(t, search)
t.Cleanup(search.Stop)
// Create a new resource backend
storage, _ := newTestBackend(t, false, 0)
require.NotNil(t, storage)
// Create a new resource backend
storage, _ := newTestBackend(t, false, 0)
require.NotNil(t, storage)
// Run the shared storage and search tests
unitest.RunTestSearchAndStorage(t, ctx, storage, search)
})
}
// Run the shared storage and search tests
unitest.RunTestSearchAndStorage(t, ctx, storage, search)
}
func TestClientServer(t *testing.T) {
@@ -24,24 +24,6 @@ func TestMain(m *testing.M) {
testsuite.Run(m)
}
// mockElasticsearchHandler returns a handler that mocks Elasticsearch endpoints.
// It responds to GET / with cluster info (required for datasource initialization)
// and returns 401 Unauthorized for all other requests.
func mockElasticsearchHandler(onRequest func(r *http.Request)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
switch {
case r.Method == http.MethodGet && r.URL.Path == "/":
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"version":{"build_flavor":"default","number":"8.0.0"}}`))
default:
if onRequest != nil {
onRequest(r)
}
w.WriteHeader(http.StatusUnauthorized)
}
}
}
func TestIntegrationElasticsearch(t *testing.T) {
testutil.SkipIntegrationTestInShortMode(t)
@@ -53,8 +35,9 @@ func TestIntegrationElasticsearch(t *testing.T) {
ctx := context.Background()
var outgoingRequest *http.Request
outgoingServer := httptest.NewServer(mockElasticsearchHandler(func(r *http.Request) {
outgoingServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
outgoingRequest = r
w.WriteHeader(http.StatusUnauthorized)
}))
t.Cleanup(outgoingServer.Close)
@@ -209,7 +209,7 @@
"path": "public/plugins/grafana-azure-monitor-datasource/img/azure_monitor_cpu.png"
}
],
"version": "12.4.0-pre",
"version": "12.3.0-pre",
"updated": "",
"keywords": [
"azure",
@@ -589,7 +589,7 @@
"hasUpdate": false,
"defaultNavUrl": "/plugins/datagrid/",
"category": "",
"state": "deprecated",
"state": "beta",
"signature": "internal",
"signatureType": "",
"signatureOrg": "",
@@ -639,7 +639,7 @@
]
},
"dependencies": {
"grafanaDependency": "\u003e=11.6.0",
"grafanaDependency": "",
"grafanaVersion": "*",
"plugins": [],
"extensions": {
@@ -880,7 +880,7 @@
},
"build": {},
"screenshots": null,
"version": "12.4.0-pre",
"version": "12.3.0-pre",
"updated": "",
"keywords": null
},
@@ -934,7 +934,7 @@
},
"build": {},
"screenshots": null,
"version": "12.4.0-pre",
"version": "12.3.0-pre",
"updated": "",
"keywords": [
"grafana",
@@ -1000,7 +1000,7 @@
},
"build": {},
"screenshots": null,
"version": "12.4.0-pre",
"version": "12.3.0-pre",
"updated": "",
"keywords": null
},
@@ -1217,7 +1217,7 @@
},
"build": {},
"screenshots": null,
"version": "12.4.0-pre",
"version": "12.3.0-pre",
"updated": "",
"keywords": null
},
@@ -1325,7 +1325,7 @@
},
"build": {},
"screenshots": null,
"version": "12.4.0-pre",
"version": "12.3.0-pre",
"updated": "",
"keywords": null
},
@@ -1375,7 +1375,7 @@
},
"build": {},
"screenshots": null,
"version": "12.4.0-pre",
"version": "12.3.0-pre",
"updated": "",
"keywords": null
},
@@ -1425,7 +1425,7 @@
},
"build": {},
"screenshots": null,
"version": "12.4.0-pre",
"version": "12.3.0-pre",
"updated": "",
"keywords": null
},
@@ -1575,7 +1575,7 @@
},
"build": {},
"screenshots": null,
"version": "12.4.0-pre",
"version": "",
"updated": "",
"keywords": null
},
@@ -1629,7 +1629,7 @@
},
"build": {},
"screenshots": null,
"version": "12.4.0-pre",
"version": "12.3.0-pre",
"updated": "",
"keywords": [
"grafana",
@@ -1734,7 +1734,7 @@
},
"build": {},
"screenshots": null,
"version": "12.4.0-pre",
"version": "12.3.0-pre",
"updated": "",
"keywords": null
},
@@ -2042,7 +2042,7 @@
},
"build": {},
"screenshots": null,
"version": "12.4.0-pre",
"version": "12.3.0-pre",
"updated": "",
"keywords": null
},
@@ -2092,7 +2092,7 @@
},
"build": {},
"screenshots": null,
"version": "12.4.0-pre",
"version": "12.3.0-pre",
"updated": "",
"keywords": null
},
@@ -2445,7 +2445,7 @@
},
"build": {},
"screenshots": null,
"version": "12.4.0-pre",
"version": "12.3.0-pre",
"updated": "",
"keywords": null
},
+19 -39
View File
@@ -97,7 +97,7 @@ func TestIntegrationSearchDevDashboards(t *testing.T) {
require.Equal(t, 16, fileCount, "file count from %s", devenv)
// Helper to call search
callSearch := func(user apis.User, params map[string]string) dashboardV0.SearchResults {
callSearch := func(user apis.User, params string) dashboardV0.SearchResults {
require.NotNil(t, user)
ns := user.Identity.GetNamespace()
cfg := dynamic.ConfigFor(user.NewRestConfig())
@@ -107,12 +107,17 @@ func TestIntegrationSearchDevDashboards(t *testing.T) {
var statusCode int
req := restClient.Get().AbsPath("apis", "dashboard.grafana.app", "v0alpha1", "namespaces", ns, "search").
//Param("explain", "true") // helpful to understand which field made things match
Param("limit", "1000").
Param("type", "dashboard") // Only search dashboards
for k, v := range params {
req = req.Param(k, v)
for kv := range strings.SplitSeq(params, "&") {
if kv == "" {
continue
}
parts := strings.SplitN(kv, "=", 2)
if len(parts) == 2 {
req = req.Param(parts[0], parts[1])
}
}
res := req.Do(ctx).StatusCode(&statusCode)
require.NoError(t, res.Error())
@@ -135,47 +140,22 @@ func TestIntegrationSearchDevDashboards(t *testing.T) {
testCases := []struct {
name string
user apis.User
params map[string]string
params string
}{
{
name: "all",
user: helper.Org1.Admin,
name: "all",
user: helper.Org1.Admin,
params: "", // only dashboards
},
{
name: "query-single-word",
user: helper.Org1.Admin,
params: map[string]string{
"query": "stacking",
},
name: "simple-query",
user: helper.Org1.Admin,
params: "query=stacking",
},
{
name: "query-multiple-words",
user: helper.Org1.Admin,
params: map[string]string{
"query": "graph softMin", // must match ALL terms
},
},
{
name: "with-text-panel",
user: helper.Org1.Admin,
params: map[string]string{
"field": "panel_types", // return panel types
"panelType": "text",
},
},
{
name: "title-ngram-prefix",
user: helper.Org1.Admin,
params: map[string]string{
"query": "zer", // should match "Zero Decimals Y Ticks"
},
},
{
name: "title-ngram-middle-word",
user: helper.Org1.Admin,
params: map[string]string{
"query": "decim", // should match "Zero Decimals Y Ticks"
},
name: "with-text-panel",
user: helper.Org1.Admin,
params: "field=panel_types&panelType=text",
},
}
for i, tc := range testCases {
@@ -10,7 +10,7 @@
"panel-tests",
"graph-ng"
],
"score": 0.284
"score": 0.658
},
{
"resource": "dashboards",
@@ -21,8 +21,8 @@
"panel-tests",
"graph-ng"
],
"score": 0.269
"score": 0.625
}
],
"maxScore": 0.284
"maxScore": 0.658
}
@@ -1,17 +0,0 @@
{
"totalHits": 1,
"hits": [
{
"resource": "dashboards",
"name": "timeseries-soft-limits",
"title": "Panel Tests - Graph NG - softMin/softMax",
"tags": [
"gdev",
"panel-tests",
"graph-ng"
],
"score": 0.024
}
],
"maxScore": 0.024
}
@@ -1,17 +0,0 @@
{
"totalHits": 1,
"hits": [
{
"resource": "dashboards",
"name": "timeseries-y-ticks-zero-decimals",
"title": "Zero Decimals Y Ticks",
"tags": [
"gdev",
"panel-tests",
"graph-ng"
],
"score": 0.35
}
],
"maxScore": 0.35
}
@@ -1,17 +0,0 @@
{
"totalHits": 1,
"hits": [
{
"resource": "dashboards",
"name": "timeseries-y-ticks-zero-decimals",
"title": "Zero Decimals Y Ticks",
"tags": [
"gdev",
"panel-tests",
"graph-ng"
],
"score": 0.35
}
],
"maxScore": 0.35
}
+183 -99
View File
@@ -5,6 +5,8 @@ import (
"encoding/json"
"errors"
"fmt"
"maps"
"slices"
"testing"
"github.com/stretchr/testify/require"
@@ -13,9 +15,11 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/apimachinery/utils"
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
"github.com/grafana/grafana/pkg/services/datasources"
"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/tests/testsuite"
@@ -28,112 +32,192 @@ func TestMain(m *testing.M) {
func TestIntegrationTestDatasource(t *testing.T) {
testutil.SkipIntegrationTestInShortMode(t)
expectedAPIVersion := "grafana-testdata-datasource.datasource.grafana.app/v0alpha1"
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
AppModeProduction: false, // dev mode required for datasource connections
DisableAnonymous: true,
EnableFeatureToggles: []string{
featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs, // Required to start the example service
},
})
for _, mode := range []grafanarest.DualWriterMode{
grafanarest.Mode0, // Legacy only
grafanarest.Mode2, // write both, read legacy
grafanarest.Mode3, // write both, read unified
grafanarest.Mode5, // Unified only
} {
t.Run(fmt.Sprintf("testdata (mode:%d)", mode), func(t *testing.T) {
ctx := context.Background()
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
DisableAnonymous: true,
EnableFeatureToggles: []string{
featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs, // Required to start the datasource api servers
featuremgmt.FlagQueryServiceWithConnections, // enables CRUD endpoints
},
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
"datasources.grafana-testdata-datasource.datasource.grafana.app": {
DualWriterMode: mode,
},
},
})
// Create a single datasource
ds := helper.CreateDS(&datasources.AddDataSourceCommand{
Name: "test",
Type: datasources.DS_TESTDATA,
UID: "test",
OrgID: int64(1),
client := helper.Org1.Admin.ResourceClient(t, schema.GroupVersionResource{
Group: "grafana-testdata-datasource.datasource.grafana.app",
Version: "v0alpha1",
Resource: "datasources",
}).Namespace("default")
// These settings are not actually used, but testing that they get saved
Database: "testdb",
URL: "http://fake.url",
Access: datasources.DS_ACCESS_PROXY,
User: "example",
ReadOnly: true,
JsonData: simplejson.NewFromAny(map[string]any{
"hello": "world",
}),
SecureJsonData: map[string]string{
"aaa": "AAA",
"bbb": "BBB",
},
})
require.Equal(t, "test", ds.UID)
t.Run("create", func(t *testing.T) {
out, err := client.Create(ctx, &unstructured.Unstructured{
Object: map[string]any{
"apiVersion": "grafana-testdata-datasource.datasource.grafana.app/v0alpha1",
"kind": "DataSource",
"metadata": map[string]any{
"name": "test",
},
"spec": map[string]any{
"title": "test",
},
"secure": map[string]any{
"aaa": map[string]any{
"create": "AAA",
},
"bbb": map[string]any{
"create": "BBB",
},
},
},
}, metav1.CreateOptions{})
require.NoError(t, err)
require.Equal(t, "test", out.GetName())
require.Equal(t, expectedAPIVersion, out.GetAPIVersion())
t.Run("Admin configs", func(t *testing.T) {
client := helper.Org1.Admin.ResourceClient(t, schema.GroupVersionResource{
Group: "grafana-testdata-datasource.datasource.grafana.app",
Version: "v0alpha1",
Resource: "datasources",
}).Namespace("default")
ctx := context.Background()
obj, err := utils.MetaAccessor(out)
require.NoError(t, err)
list, err := client.List(ctx, metav1.ListOptions{})
require.NoError(t, err)
require.Len(t, list.Items, 1, "expected a single connection")
require.Equal(t, "test", list.Items[0].GetName(), "with the test uid")
secure, err := obj.GetSecureValues()
require.NoError(t, err)
spec, _, _ := unstructured.NestedMap(list.Items[0].Object, "spec")
jj, _ := json.MarshalIndent(spec, "", " ")
fmt.Printf("%s\n", string(jj))
require.JSONEq(t, `{
"access": "proxy",
"database": "testdb",
"isDefault": true,
"jsonData": {
"hello": "world"
},
"readOnly": true,
"title": "test",
"url": "http://fake.url",
"user": "example"
}`, string(jj))
})
keys := slices.Collect(maps.Keys(secure))
require.ElementsMatch(t, []string{"aaa", "bbb"}, keys)
})
t.Run("Call subresources", func(t *testing.T) {
client := helper.Org1.Admin.ResourceClient(t, schema.GroupVersionResource{
Group: "grafana-testdata-datasource.datasource.grafana.app",
Version: "v0alpha1",
Resource: "datasources",
}).Namespace("default")
ctx := context.Background()
t.Run("update", func(t *testing.T) {
out, err := client.Update(ctx, &unstructured.Unstructured{
Object: map[string]any{
"apiVersion": "grafana-testdata-datasource.datasource.grafana.app/v0alpha1",
"metadata": map[string]any{
"name": "test",
},
"spec": map[string]any{
"title": "test",
"database": "testdb",
"url": "http://fake.url",
"access": datasources.DS_ACCESS_PROXY,
"user": "example",
"isDefault": true,
"readOnly": true,
"jsonData": map[string]any{
"hello": "world",
},
},
"secure": map[string]any{
// "aaa": map[string]any{
// "remove": true, // remove does not really remove in legacy!
// },
"ccc": map[string]any{
"create": "CCC", // add a third value
},
},
},
}, metav1.UpdateOptions{})
require.NoError(t, err)
require.Equal(t, "test", out.GetName())
require.Equal(t, expectedAPIVersion, out.GetAPIVersion())
list, err := client.List(ctx, metav1.ListOptions{})
require.NoError(t, err)
require.Len(t, list.Items, 1, "expected a single connection")
require.Equal(t, "test", list.Items[0].GetName(), "with the test uid")
obj, err := utils.MetaAccessor(out)
require.NoError(t, err)
_, err = client.Get(ctx, "test", metav1.GetOptions{}, "health")
// endpoint is disabled currently because it has not been
// sufficiently tested.
// for more info see pkg/registry/apis/datasource/sub_health.go
require.Error(t, err)
var statusErr *apierrors.StatusError
require.True(t, errors.As(err, &statusErr))
require.Equal(t, int32(501), statusErr.ErrStatus.Code)
// require.NoError(t, err)
// body, err := rsp.MarshalJSON()
// require.NoError(t, err)
// //fmt.Printf("GOT: %v\n", string(body))
// require.JSONEq(t, `{
// "apiVersion": "testdata.datasource.grafana.app/v0alpha1",
// "code": 1,
// "kind": "HealthCheckResult",
// "message": "Data source is working",
// "status": "OK"
// }
// `, string(body))
secure, err := obj.GetSecureValues()
require.NoError(t, err)
// Test connecting to non-JSON marshaled data
raw := apis.DoRequest[any](helper, apis.RequestParams{
User: helper.Org1.Admin,
Method: "GET",
Path: "/apis/grafana-testdata-datasource.datasource.grafana.app/v0alpha1/namespaces/default/datasources/test/resource",
}, nil)
// endpoint is disabled currently because it has not been
// sufficiently tested.
// for more info see pkg/registry/apis/datasource/sub_resource.go
require.Equal(t, int32(501), raw.Status.Code)
// require.Equal(t, `Hello world from test datasource!`, string(raw.Body))
})
keys := slices.Collect(maps.Keys(secure))
require.ElementsMatch(t, []string{"aaa", "bbb", "ccc"}, keys)
})
t.Run("list", func(t *testing.T) {
list, err := client.List(ctx, metav1.ListOptions{})
require.NoError(t, err)
require.Equal(t, expectedAPIVersion, list.GetAPIVersion())
require.Len(t, list.Items, 1, "expected a single datasource")
require.Equal(t, "test", list.Items[0].GetName(), "with the test uid")
spec, _, _ := unstructured.NestedMap(list.Items[0].Object, "spec")
jj, _ := json.MarshalIndent(spec, "", " ")
// fmt.Printf("%s\n", string(jj))
require.JSONEq(t, `{
"access": "proxy",
"database": "testdb",
"isDefault": true,
"jsonData": {
"hello": "world"
},
"readOnly": true,
"title": "test",
"url": "http://fake.url",
"user": "example"
}`, string(jj))
})
t.Run("execute", func(t *testing.T) {
client := helper.Org1.Admin.ResourceClient(t, schema.GroupVersionResource{
Group: "grafana-testdata-datasource.datasource.grafana.app",
Version: "v0alpha1",
Resource: "datasources",
}).Namespace("default")
ctx := context.Background()
list, err := client.List(ctx, metav1.ListOptions{})
require.NoError(t, err)
require.Len(t, list.Items, 1, "expected a single connection")
require.Equal(t, "test", list.Items[0].GetName(), "with the test uid")
_, err = client.Get(ctx, "test", metav1.GetOptions{}, "health")
// endpoint is disabled currently because it has not been
// sufficiently tested.
// for more info see pkg/registry/apis/datasource/sub_health.go
require.Error(t, err)
var statusErr *apierrors.StatusError
require.True(t, errors.As(err, &statusErr))
require.Equal(t, int32(501), statusErr.ErrStatus.Code)
// require.NoError(t, err)
// body, err := rsp.MarshalJSON()
// require.NoError(t, err)
// //fmt.Printf("GOT: %v\n", string(body))
// require.JSONEq(t, `{
// "apiVersion": "grafana-testdata-datasource.datasource.grafana.app/v0alpha1",
// "code": 1,
// "kind": "HealthCheckResult",
// "message": "Data source is working",
// "status": "OK"
// }
// `, string(body))
// Test connecting to non-JSON marshaled data
raw := apis.DoRequest[any](helper, apis.RequestParams{
User: helper.Org1.Admin,
Method: "GET",
Path: "/apis/grafana-testdata-datasource.datasource.grafana.app/v0alpha1/namespaces/default/datasources/test/resource",
}, nil)
// endpoint is disabled currently because it has not been
// sufficiently tested.
// for more info see pkg/registry/apis/datasource/sub_resource.go
require.Equal(t, int32(501), raw.Status.Code)
// require.Equal(t, `Hello world from test datasource!`, string(raw.Body))
})
t.Run("delete", func(t *testing.T) {
err := client.Delete(ctx, "test", metav1.DeleteOptions{})
require.NoError(t, err)
list, err := client.List(ctx, metav1.ListOptions{})
require.NoError(t, err)
require.Empty(t, list.Items)
})
})
}
}
@@ -103,6 +103,98 @@
],
"description": "list objects of kind DataSource",
"operationId": "listDataSource",
"parameters": [
{
"name": "allowWatchBookmarks",
"in": "query",
"description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.",
"schema": {
"type": "boolean",
"uniqueItems": true
}
},
{
"name": "continue",
"in": "query",
"description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"name": "fieldSelector",
"in": "query",
"description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"name": "labelSelector",
"in": "query",
"description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"name": "limit",
"in": "query",
"description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.",
"schema": {
"type": "integer",
"uniqueItems": true
}
},
{
"name": "resourceVersion",
"in": "query",
"description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"name": "resourceVersionMatch",
"in": "query",
"description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"name": "sendInitialEvents",
"in": "query",
"description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.",
"schema": {
"type": "boolean",
"uniqueItems": true
}
},
{
"name": "timeoutSeconds",
"in": "query",
"description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.",
"schema": {
"type": "integer",
"uniqueItems": true
}
},
{
"name": "watch",
"in": "query",
"description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.",
"schema": {
"type": "boolean",
"uniqueItems": true
}
}
],
"responses": {
"200": {
"description": "OK",
@@ -142,52 +234,285 @@
"kind": "DataSource"
}
},
"post": {
"tags": [
"DataSource"
],
"description": "create a DataSource",
"operationId": "createDataSource",
"parameters": [
{
"name": "dryRun",
"in": "query",
"description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"name": "fieldManager",
"in": "query",
"description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"name": "fieldValidation",
"in": "query",
"description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.",
"schema": {
"type": "string",
"uniqueItems": true
}
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource"
}
},
"application/vnd.kubernetes.protobuf": {
"schema": {
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource"
}
},
"application/yaml": {
"schema": {
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource"
}
},
"application/vnd.kubernetes.protobuf": {
"schema": {
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource"
}
},
"application/yaml": {
"schema": {
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource"
}
}
}
},
"201": {
"description": "Created",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource"
}
},
"application/vnd.kubernetes.protobuf": {
"schema": {
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource"
}
},
"application/yaml": {
"schema": {
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource"
}
}
}
},
"202": {
"description": "Accepted",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource"
}
},
"application/vnd.kubernetes.protobuf": {
"schema": {
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource"
}
},
"application/yaml": {
"schema": {
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource"
}
}
}
}
},
"x-kubernetes-action": "post",
"x-kubernetes-group-version-kind": {
"group": "grafana-testdata-datasource.datasource.grafana.app",
"version": "v0alpha1",
"kind": "DataSource"
}
},
"delete": {
"tags": [
"DataSource"
],
"description": "delete collection of DataSource",
"operationId": "deletecollectionDataSource",
"parameters": [
{
"name": "continue",
"in": "query",
"description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"name": "dryRun",
"in": "query",
"description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"name": "fieldSelector",
"in": "query",
"description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"name": "gracePeriodSeconds",
"in": "query",
"description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.",
"schema": {
"type": "integer",
"uniqueItems": true
}
},
{
"name": "ignoreStoreReadErrorWithClusterBreakingPotential",
"in": "query",
"description": "if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it",
"schema": {
"type": "boolean",
"uniqueItems": true
}
},
{
"name": "labelSelector",
"in": "query",
"description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"name": "limit",
"in": "query",
"description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.",
"schema": {
"type": "integer",
"uniqueItems": true
}
},
{
"name": "orphanDependents",
"in": "query",
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"schema": {
"type": "boolean",
"uniqueItems": true
}
},
{
"name": "propagationPolicy",
"in": "query",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"name": "resourceVersion",
"in": "query",
"description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"name": "resourceVersionMatch",
"in": "query",
"description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"name": "sendInitialEvents",
"in": "query",
"description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.",
"schema": {
"type": "boolean",
"uniqueItems": true
}
},
{
"name": "timeoutSeconds",
"in": "query",
"description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.",
"schema": {
"type": "integer",
"uniqueItems": true
}
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status"
}
},
"application/vnd.kubernetes.protobuf": {
"schema": {
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status"
}
},
"application/yaml": {
"schema": {
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status"
}
}
}
}
},
"x-kubernetes-action": "deletecollection",
"x-kubernetes-group-version-kind": {
"group": "grafana-testdata-datasource.datasource.grafana.app",
"version": "v0alpha1",
"kind": "DataSource"
}
},
"parameters": [
{
"name": "allowWatchBookmarks",
"in": "query",
"description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.",
"schema": {
"type": "boolean",
"uniqueItems": true
}
},
{
"name": "continue",
"in": "query",
"description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"name": "fieldSelector",
"in": "query",
"description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"name": "labelSelector",
"in": "query",
"description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"name": "limit",
"in": "query",
"description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.",
"schema": {
"type": "integer",
"uniqueItems": true
}
},
{
"name": "namespace",
"in": "path",
@@ -206,51 +531,6 @@
"type": "string",
"uniqueItems": true
}
},
{
"name": "resourceVersion",
"in": "query",
"description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"name": "resourceVersionMatch",
"in": "query",
"description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"name": "sendInitialEvents",
"in": "query",
"description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.",
"schema": {
"type": "boolean",
"uniqueItems": true
}
},
{
"name": "timeoutSeconds",
"in": "query",
"description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.",
"schema": {
"type": "integer",
"uniqueItems": true
}
},
{
"name": "watch",
"in": "query",
"description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.",
"schema": {
"type": "boolean",
"uniqueItems": true
}
}
]
},
@@ -290,6 +570,330 @@
"kind": "DataSource"
}
},
"put": {
"tags": [
"DataSource"
],
"description": "replace the specified DataSource",
"operationId": "replaceDataSource",
"parameters": [
{
"name": "dryRun",
"in": "query",
"description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"name": "fieldManager",
"in": "query",
"description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"name": "fieldValidation",
"in": "query",
"description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.",
"schema": {
"type": "string",
"uniqueItems": true
}
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource"
}
},
"application/vnd.kubernetes.protobuf": {
"schema": {
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource"
}
},
"application/yaml": {
"schema": {
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource"
}
},
"application/vnd.kubernetes.protobuf": {
"schema": {
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource"
}
},
"application/yaml": {
"schema": {
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource"
}
}
}
},
"201": {
"description": "Created",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource"
}
},
"application/vnd.kubernetes.protobuf": {
"schema": {
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource"
}
},
"application/yaml": {
"schema": {
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource"
}
}
}
}
},
"x-kubernetes-action": "put",
"x-kubernetes-group-version-kind": {
"group": "grafana-testdata-datasource.datasource.grafana.app",
"version": "v0alpha1",
"kind": "DataSource"
}
},
"delete": {
"tags": [
"DataSource"
],
"description": "delete a DataSource",
"operationId": "deleteDataSource",
"parameters": [
{
"name": "dryRun",
"in": "query",
"description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"name": "gracePeriodSeconds",
"in": "query",
"description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.",
"schema": {
"type": "integer",
"uniqueItems": true
}
},
{
"name": "ignoreStoreReadErrorWithClusterBreakingPotential",
"in": "query",
"description": "if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it",
"schema": {
"type": "boolean",
"uniqueItems": true
}
},
{
"name": "orphanDependents",
"in": "query",
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"schema": {
"type": "boolean",
"uniqueItems": true
}
},
{
"name": "propagationPolicy",
"in": "query",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.",
"schema": {
"type": "string",
"uniqueItems": true
}
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status"
}
},
"application/vnd.kubernetes.protobuf": {
"schema": {
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status"
}
},
"application/yaml": {
"schema": {
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status"
}
}
}
},
"202": {
"description": "Accepted",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status"
}
},
"application/vnd.kubernetes.protobuf": {
"schema": {
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status"
}
},
"application/yaml": {
"schema": {
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status"
}
}
}
}
},
"x-kubernetes-action": "delete",
"x-kubernetes-group-version-kind": {
"group": "grafana-testdata-datasource.datasource.grafana.app",
"version": "v0alpha1",
"kind": "DataSource"
}
},
"patch": {
"tags": [
"DataSource"
],
"description": "partially update the specified DataSource",
"operationId": "updateDataSource",
"parameters": [
{
"name": "dryRun",
"in": "query",
"description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"name": "fieldManager",
"in": "query",
"description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"name": "fieldValidation",
"in": "query",
"description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"name": "force",
"in": "query",
"description": "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.",
"schema": {
"type": "boolean",
"uniqueItems": true
}
}
],
"requestBody": {
"content": {
"application/apply-patch+yaml": {
"schema": {
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch"
}
},
"application/json-patch+json": {
"schema": {
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch"
}
},
"application/merge-patch+json": {
"schema": {
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch"
}
},
"application/strategic-merge-patch+json": {
"schema": {
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource"
}
},
"application/vnd.kubernetes.protobuf": {
"schema": {
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource"
}
},
"application/yaml": {
"schema": {
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource"
}
}
}
},
"201": {
"description": "Created",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource"
}
},
"application/vnd.kubernetes.protobuf": {
"schema": {
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource"
}
},
"application/yaml": {
"schema": {
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource"
}
}
}
}
},
"x-kubernetes-action": "patch",
"x-kubernetes-group-version-kind": {
"group": "grafana-testdata-datasource.datasource.grafana.app",
"version": "v0alpha1",
"kind": "DataSource"
}
},
"parameters": [
{
"name": "name",
@@ -946,6 +1550,54 @@
}
}
},
"io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions": {
"description": "DeleteOptions may be provided when deleting an API object.",
"type": "object",
"properties": {
"apiVersion": {
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
"type": "string"
},
"dryRun": {
"description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed",
"type": "array",
"items": {
"type": "string",
"default": ""
},
"x-kubernetes-list-type": "atomic"
},
"gracePeriodSeconds": {
"description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.",
"type": "integer",
"format": "int64"
},
"ignoreStoreReadErrorWithClusterBreakingPotential": {
"description": "if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it",
"type": "boolean"
},
"kind": {
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
"type": "string"
},
"orphanDependents": {
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"type": "boolean"
},
"preconditions": {
"description": "Must be fulfilled before a deletion is carried out. If not possible, a 409 Conflict status will be returned.",
"allOf": [
{
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Preconditions"
}
]
},
"propagationPolicy": {
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.",
"type": "string"
}
}
},
"io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1": {
"description": "FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.\n\nEach key is either a '.' representing the field itself, and will always map to an empty set, or a string representing a sub-field or item. The string will follow one of these four formats: 'f:\u003cname\u003e', where \u003cname\u003e is the name of a field in a struct, or key in a map 'v:\u003cvalue\u003e', where \u003cvalue\u003e is the exact json formatted value of a list item 'i:\u003cindex\u003e', where \u003cindex\u003e is position of a item in a list 'k:\u003ckeys\u003e', where \u003ckeys\u003e is a map of a list item's key fields to their unique values If a key maps to an empty Fields value, the field that key represents is part of the set.\n\nThe exact format is defined in sigs.k8s.io/structured-merge-diff",
"type": "object"
@@ -1169,6 +1821,131 @@
},
"x-kubernetes-map-type": "atomic"
},
"io.k8s.apimachinery.pkg.apis.meta.v1.Patch": {
"description": "Patch is provided to give a concrete name and type to the Kubernetes PATCH request body.",
"type": "object"
},
"io.k8s.apimachinery.pkg.apis.meta.v1.Preconditions": {
"description": "Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.",
"type": "object",
"properties": {
"resourceVersion": {
"description": "Specifies the target ResourceVersion",
"type": "string"
},
"uid": {
"description": "Specifies the target UID.",
"type": "string"
}
}
},
"io.k8s.apimachinery.pkg.apis.meta.v1.Status": {
"description": "Status is a return value for calls that don't return other objects.",
"type": "object",
"properties": {
"apiVersion": {
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
"type": "string"
},
"code": {
"description": "Suggested HTTP return code for this status, 0 if not set.",
"type": "integer",
"format": "int32"
},
"details": {
"description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.",
"allOf": [
{
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails"
}
],
"x-kubernetes-list-type": "atomic"
},
"kind": {
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
"type": "string"
},
"message": {
"description": "A human-readable description of the status of this operation.",
"type": "string"
},
"metadata": {
"description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
"default": {},
"allOf": [
{
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta"
}
]
},
"reason": {
"description": "A machine-readable description of why this operation is in the \"Failure\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.",
"type": "string"
},
"status": {
"description": "Status of the operation. One of: \"Success\" or \"Failure\". More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status",
"type": "string"
}
}
},
"io.k8s.apimachinery.pkg.apis.meta.v1.StatusCause": {
"description": "StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.",
"type": "object",
"properties": {
"field": {
"description": "The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.\n\nExamples:\n \"name\" - the field \"name\" on the current resource\n \"items[0].name\" - the field \"name\" on the first array entry in \"items\"",
"type": "string"
},
"message": {
"description": "A human-readable description of the cause of the error. This field may be presented as-is to a reader.",
"type": "string"
},
"reason": {
"description": "A machine-readable description of the cause of the error. If this value is empty there is no information available.",
"type": "string"
}
}
},
"io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails": {
"description": "StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.",
"type": "object",
"properties": {
"causes": {
"description": "The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.",
"type": "array",
"items": {
"default": {},
"allOf": [
{
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusCause"
}
]
},
"x-kubernetes-list-type": "atomic"
},
"group": {
"description": "The group attribute of the resource associated with the status StatusReason.",
"type": "string"
},
"kind": {
"description": "The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
"type": "string"
},
"name": {
"description": "The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).",
"type": "string"
},
"retryAfterSeconds": {
"description": "If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action.",
"type": "integer",
"format": "int32"
},
"uid": {
"description": "UID of the resource. (when there is a single resource which can be described). More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids",
"type": "string"
}
}
},
"io.k8s.apimachinery.pkg.apis.meta.v1.Time": {
"description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.",
"type": "string",
+3 -6
View File
@@ -92,7 +92,7 @@ func (s *Service) CheckHealth(ctx context.Context, req *backend.CheckHealthReque
}, nil
}
url := fmt.Sprintf("%s/v3/projects/%s/metricDescriptors", dsInfo.services[cloudMonitor].url, defaultProject)
url := fmt.Sprintf("%v/v3/projects/%v/metricDescriptors", dsInfo.services[cloudMonitor].url, defaultProject)
request, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, err
@@ -139,7 +139,6 @@ type datasourceInfo struct {
defaultProject string
clientEmail string
tokenUri string
universeDomain string
services map[string]datasourceService
privateKey string
usingImpersonation bool
@@ -151,7 +150,6 @@ type datasourceJSONData struct {
DefaultProject string `json:"defaultProject"`
ClientEmail string `json:"clientEmail"`
TokenURI string `json:"tokenUri"`
UniverseDomain string `json:"universeDomain"`
UsingImpersonation bool `json:"usingImpersonation"`
ServiceAccountToImpersonate string `json:"serviceAccountToImpersonate"`
}
@@ -181,7 +179,6 @@ func newInstanceSettings(httpClientProvider httpclient.Provider) datasource.Inst
defaultProject: jsonData.DefaultProject,
clientEmail: jsonData.ClientEmail,
tokenUri: jsonData.TokenURI,
universeDomain: jsonData.UniverseDomain,
usingImpersonation: jsonData.UsingImpersonation,
serviceAccountToImpersonate: jsonData.ServiceAccountToImpersonate,
services: map[string]datasourceService{},
@@ -197,13 +194,13 @@ func newInstanceSettings(httpClientProvider httpclient.Provider) datasource.Inst
return nil, err
}
for name := range routes {
for name, info := range routes {
client, err := newHTTPClient(dsInfo, opts, &httpClientProvider, name)
if err != nil {
return nil, err
}
dsInfo.services[name] = datasourceService{
url: buildURL(name, dsInfo.universeDomain),
url: info.url,
client: client,
}
}
+2 -9
View File
@@ -23,12 +23,12 @@ type routeInfo struct {
var routes = map[string]routeInfo{
cloudMonitor: {
method: "GET",
url: "https://monitoring.",
url: "https://monitoring.googleapis.com",
scopes: []string{cloudMonitorScope},
},
resourceManager: {
method: "GET",
url: "https://cloudresourcemanager.",
url: "https://cloudresourcemanager.googleapis.com",
scopes: []string{resourceManagerScope},
},
}
@@ -68,13 +68,6 @@ func getMiddleware(model *datasourceInfo, routePath string) (httpclient.Middlewa
return tokenprovider.AuthMiddleware(provider), nil
}
func buildURL(route string, universeDomain string) string {
if universeDomain == "" {
universeDomain = "googleapis.com"
}
return routes[route].url + universeDomain
}
func newHTTPClient(model *datasourceInfo, opts httpclient.Options, clientProvider *httpclient.Provider, route string) (*http.Client, error) {
m, err := getMiddleware(model, route)
if err != nil {
@@ -111,7 +111,7 @@ func Test_setRequestVariables(t *testing.T) {
im: &fakeInstance{
services: map[string]datasourceService{
cloudMonitor: {
url: buildURL(cloudMonitor, "googleapis.com"),
url: routes[cloudMonitor].url,
client: &http.Client{},
},
},
@@ -3,8 +3,8 @@ package elasticsearch
import (
"regexp"
"github.com/grafana/grafana/pkg/components/simplejson"
es "github.com/grafana/grafana/pkg/tsdb/elasticsearch/client"
"github.com/grafana/grafana/pkg/tsdb/elasticsearch/simplejson"
)
// addDateHistogramAgg adds a date histogram aggregation to the aggregation builder
+3 -7
View File
@@ -16,6 +16,7 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
"github.com/grafana/grafana-plugin-sdk-go/backend/tracing"
"github.com/grafana/grafana/pkg/services/featuremgmt"
)
// Used in logging to mark a stage
@@ -34,7 +35,6 @@ type DatasourceInfo struct {
Interval string
MaxConcurrentShardRequests int64
IncludeFrozen bool
ClusterInfo ClusterInfo
}
type ConfiguredFields struct {
@@ -159,7 +159,7 @@ func (c *baseClientImpl) ExecuteMultisearch(r *MultiSearchRequest) (*MultiSearch
resSpan.End()
}()
improvedParsingEnabled := isFeatureEnabled(c.ctx, "elasticsearchImprovedParsing")
improvedParsingEnabled := isFeatureEnabled(c.ctx, featuremgmt.FlagElasticsearchImprovedParsing)
msr, err := c.parser.parseMultiSearchResponse(res.Body, improvedParsingEnabled)
if err != nil {
return nil, err
@@ -197,11 +197,7 @@ func (c *baseClientImpl) createMultiSearchRequests(searchRequests []*SearchReque
func (c *baseClientImpl) getMultiSearchQueryParameters() string {
var qs []string
// if the build flavor is not serverless, we can use the max concurrent shard requests
// this is because serverless clusters do not support max concurrent shard requests
if !c.ds.ClusterInfo.IsServerless() && c.ds.MaxConcurrentShardRequests > 0 {
qs = append(qs, fmt.Sprintf("max_concurrent_shard_requests=%d", c.ds.MaxConcurrentShardRequests))
}
qs = append(qs, fmt.Sprintf("max_concurrent_shard_requests=%d", c.ds.MaxConcurrentShardRequests))
if c.ds.IncludeFrozen {
qs = append(qs, "ignore_throttled=false")
+1 -1
View File
@@ -15,7 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/tsdb/elasticsearch/simplejson"
"github.com/grafana/grafana/pkg/components/simplejson"
)
func TestClient_ExecuteMultisearch(t *testing.T) {
@@ -1,51 +0,0 @@
package es
import (
"encoding/json"
"fmt"
"net/http"
)
type VersionInfo struct {
BuildFlavor string `json:"build_flavor"`
}
// ClusterInfo represents Elasticsearch cluster information returned from the root endpoint.
// It is used to determine cluster capabilities and configuration like whether the cluster is serverless.
type ClusterInfo struct {
Version VersionInfo `json:"version"`
}
const (
BuildFlavorServerless = "serverless"
)
// GetClusterInfo fetches cluster information from the Elasticsearch root endpoint.
// It returns the cluster build flavor which is used to determine if the cluster is serverless.
func GetClusterInfo(httpCli *http.Client, url string) (clusterInfo ClusterInfo, err error) {
resp, err := httpCli.Get(url)
if err != nil {
return ClusterInfo{}, fmt.Errorf("error getting ES cluster info: %w", err)
}
if resp.StatusCode != http.StatusOK {
return ClusterInfo{}, fmt.Errorf("unexpected status code %d getting ES cluster info", resp.StatusCode)
}
defer func() {
if closeErr := resp.Body.Close(); closeErr != nil && err == nil {
err = fmt.Errorf("error closing response body: %w", closeErr)
}
}()
err = json.NewDecoder(resp.Body).Decode(&clusterInfo)
if err != nil {
return ClusterInfo{}, fmt.Errorf("error decoding ES cluster info: %w", err)
}
return clusterInfo, nil
}
func (ci ClusterInfo) IsServerless() bool {
return ci.Version.BuildFlavor == BuildFlavorServerless
}
@@ -1,188 +0,0 @@
package es
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestGetClusterInfo(t *testing.T) {
t.Run("Should successfully get cluster info", func(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
rw.Header().Set("Content-Type", "application/json")
_, err := rw.Write([]byte(`{
"name": "test-cluster",
"cluster_name": "elasticsearch",
"cluster_uuid": "abc123",
"version": {
"number": "8.0.0",
"build_flavor": "default",
"build_type": "tar",
"build_hash": "abc123",
"build_date": "2023-01-01T00:00:00.000Z",
"build_snapshot": false,
"lucene_version": "9.0.0"
}
}`))
require.NoError(t, err)
}))
t.Cleanup(func() {
ts.Close()
})
clusterInfo, err := GetClusterInfo(ts.Client(), ts.URL)
require.NoError(t, err)
require.NotNil(t, clusterInfo)
assert.Equal(t, "default", clusterInfo.Version.BuildFlavor)
})
t.Run("Should successfully get serverless cluster info", func(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
rw.Header().Set("Content-Type", "application/json")
_, err := rw.Write([]byte(`{
"name": "serverless-cluster",
"cluster_name": "elasticsearch",
"cluster_uuid": "def456",
"version": {
"number": "8.11.0",
"build_flavor": "serverless",
"build_type": "docker",
"build_hash": "def456",
"build_date": "2023-11-01T00:00:00.000Z",
"build_snapshot": false,
"lucene_version": "9.8.0"
}
}`))
require.NoError(t, err)
}))
t.Cleanup(func() {
ts.Close()
})
clusterInfo, err := GetClusterInfo(ts.Client(), ts.URL)
require.NoError(t, err)
require.NotNil(t, clusterInfo)
assert.Equal(t, "serverless", clusterInfo.Version.BuildFlavor)
assert.True(t, clusterInfo.IsServerless())
})
t.Run("Should return error when HTTP request fails", func(t *testing.T) {
clusterInfo, err := GetClusterInfo(http.DefaultClient, "http://invalid-url-that-does-not-exist.local:9999")
require.Error(t, err)
require.Equal(t, ClusterInfo{}, clusterInfo)
assert.Contains(t, err.Error(), "error getting ES cluster info")
})
t.Run("Should return error when response body is invalid JSON", func(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
rw.Header().Set("Content-Type", "application/json")
_, err := rw.Write([]byte(`{"invalid json`))
require.NoError(t, err)
}))
t.Cleanup(func() {
ts.Close()
})
clusterInfo, err := GetClusterInfo(ts.Client(), ts.URL)
require.Error(t, err)
require.Equal(t, ClusterInfo{}, clusterInfo)
assert.Contains(t, err.Error(), "error decoding ES cluster info")
})
t.Run("Should handle empty version object", func(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
rw.Header().Set("Content-Type", "application/json")
_, err := rw.Write([]byte(`{
"name": "test-cluster",
"version": {}
}`))
require.NoError(t, err)
}))
t.Cleanup(func() {
ts.Close()
})
clusterInfo, err := GetClusterInfo(ts.Client(), ts.URL)
require.NoError(t, err)
require.Equal(t, ClusterInfo{}, clusterInfo)
assert.Equal(t, "", clusterInfo.Version.BuildFlavor)
assert.False(t, clusterInfo.IsServerless())
})
t.Run("Should handle HTTP error status codes", func(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
rw.WriteHeader(http.StatusUnauthorized)
_, err := rw.Write([]byte(`{"error": "Unauthorized"}`))
require.NoError(t, err)
}))
t.Cleanup(func() {
ts.Close()
})
clusterInfo, err := GetClusterInfo(ts.Client(), ts.URL)
require.Error(t, err)
require.Equal(t, ClusterInfo{}, clusterInfo)
assert.Contains(t, err.Error(), "unexpected status code 401 getting ES cluster info")
})
}
func TestClusterInfo_IsServerless(t *testing.T) {
t.Run("Should return true when build_flavor is serverless", func(t *testing.T) {
clusterInfo := ClusterInfo{
Version: VersionInfo{
BuildFlavor: BuildFlavorServerless,
},
}
assert.True(t, clusterInfo.IsServerless())
})
t.Run("Should return false when build_flavor is default", func(t *testing.T) {
clusterInfo := ClusterInfo{
Version: VersionInfo{
BuildFlavor: "default",
},
}
assert.False(t, clusterInfo.IsServerless())
})
t.Run("Should return false when build_flavor is empty", func(t *testing.T) {
clusterInfo := ClusterInfo{
Version: VersionInfo{
BuildFlavor: "",
},
}
assert.False(t, clusterInfo.IsServerless())
})
t.Run("Should return false when build_flavor is unknown value", func(t *testing.T) {
clusterInfo := ClusterInfo{
Version: VersionInfo{
BuildFlavor: "unknown",
},
}
assert.False(t, clusterInfo.IsServerless())
})
t.Run("should return false when cluster info is empty", func(t *testing.T) {
clusterInfo := ClusterInfo{}
assert.False(t, clusterInfo.IsServerless())
})
}
@@ -8,7 +8,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/tsdb/elasticsearch/simplejson"
"github.com/grafana/grafana/pkg/components/simplejson"
)
func TestSearchRequest(t *testing.T) {
@@ -6,8 +6,8 @@ import (
"strconv"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/components/simplejson"
es "github.com/grafana/grafana/pkg/tsdb/elasticsearch/client"
"github.com/grafana/grafana/pkg/tsdb/elasticsearch/simplejson"
)
// processQuery processes a single query and adds it to the multi-search request builder
@@ -3,7 +3,7 @@ package elasticsearch
import (
"strconv"
"github.com/grafana/grafana/pkg/tsdb/elasticsearch/simplejson"
"github.com/grafana/grafana/pkg/components/simplejson"
)
// setFloatPath converts a string value at the specified path to float64
-14
View File
@@ -88,14 +88,6 @@ func newInstanceSettings(httpClientProvider *httpclient.Provider) datasource.Ins
httpCliOpts.SigV4.Service = "es"
}
apiKeyAuth, ok := jsonData["apiKeyAuth"].(bool)
if ok && apiKeyAuth {
apiKey := settings.DecryptedSecureJSONData["apiKey"]
if apiKey != "" {
httpCliOpts.Header.Add("Authorization", "ApiKey "+apiKey)
}
}
httpCli, err := httpClientProvider.New(httpCliOpts)
if err != nil {
return nil, err
@@ -159,11 +151,6 @@ func newInstanceSettings(httpClientProvider *httpclient.Provider) datasource.Ins
includeFrozen = false
}
clusterInfo, err := es.GetClusterInfo(httpCli, settings.URL)
if err != nil {
return nil, err
}
configuredFields := es.ConfiguredFields{
TimeField: timeField,
LogLevelField: logLevelField,
@@ -179,7 +166,6 @@ func newInstanceSettings(httpClientProvider *httpclient.Provider) datasource.Ins
ConfiguredFields: configuredFields,
Interval: interval,
IncludeFrozen: includeFrozen,
ClusterInfo: clusterInfo,
}
return model, nil
}
@@ -3,8 +3,6 @@ package elasticsearch
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/grafana/grafana-plugin-sdk-go/backend"
@@ -20,26 +18,8 @@ type datasourceInfo struct {
Interval string `json:"interval"`
}
// mockElasticsearchServer creates a test HTTP server that mocks Elasticsearch cluster info endpoint
func mockElasticsearchServer() *httptest.Server {
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
// Return a mock Elasticsearch cluster info response
_ = json.NewEncoder(w).Encode(map[string]interface{}{
"version": map[string]interface{}{
"build_flavor": "serverless",
"number": "8.0.0",
},
})
}))
}
func TestNewInstanceSettings(t *testing.T) {
t.Run("fields exist", func(t *testing.T) {
server := mockElasticsearchServer()
defer server.Close()
dsInfo := datasourceInfo{
TimeField: "@timestamp",
MaxConcurrentShardRequests: 5,
@@ -48,7 +28,6 @@ func TestNewInstanceSettings(t *testing.T) {
require.NoError(t, err)
dsSettings := backend.DataSourceInstanceSettings{
URL: server.URL,
JSONData: json.RawMessage(settingsJSON),
}
@@ -58,9 +37,6 @@ func TestNewInstanceSettings(t *testing.T) {
t.Run("timeField", func(t *testing.T) {
t.Run("is nil", func(t *testing.T) {
server := mockElasticsearchServer()
defer server.Close()
dsInfo := datasourceInfo{
MaxConcurrentShardRequests: 5,
Interval: "Daily",
@@ -70,7 +46,6 @@ func TestNewInstanceSettings(t *testing.T) {
require.NoError(t, err)
dsSettings := backend.DataSourceInstanceSettings{
URL: server.URL,
JSONData: json.RawMessage(settingsJSON),
}
@@ -79,9 +54,6 @@ func TestNewInstanceSettings(t *testing.T) {
})
t.Run("is empty", func(t *testing.T) {
server := mockElasticsearchServer()
defer server.Close()
dsInfo := datasourceInfo{
MaxConcurrentShardRequests: 5,
Interval: "Daily",
@@ -92,7 +64,6 @@ func TestNewInstanceSettings(t *testing.T) {
require.NoError(t, err)
dsSettings := backend.DataSourceInstanceSettings{
URL: server.URL,
JSONData: json.RawMessage(settingsJSON),
}
@@ -103,9 +74,6 @@ func TestNewInstanceSettings(t *testing.T) {
t.Run("maxConcurrentShardRequests", func(t *testing.T) {
t.Run("no maxConcurrentShardRequests", func(t *testing.T) {
server := mockElasticsearchServer()
defer server.Close()
dsInfo := datasourceInfo{
TimeField: "@timestamp",
}
@@ -113,7 +81,6 @@ func TestNewInstanceSettings(t *testing.T) {
require.NoError(t, err)
dsSettings := backend.DataSourceInstanceSettings{
URL: server.URL,
JSONData: json.RawMessage(settingsJSON),
}
@@ -123,9 +90,6 @@ func TestNewInstanceSettings(t *testing.T) {
})
t.Run("string maxConcurrentShardRequests", func(t *testing.T) {
server := mockElasticsearchServer()
defer server.Close()
dsInfo := datasourceInfo{
TimeField: "@timestamp",
MaxConcurrentShardRequests: "10",
@@ -134,7 +98,6 @@ func TestNewInstanceSettings(t *testing.T) {
require.NoError(t, err)
dsSettings := backend.DataSourceInstanceSettings{
URL: server.URL,
JSONData: json.RawMessage(settingsJSON),
}
@@ -144,9 +107,6 @@ func TestNewInstanceSettings(t *testing.T) {
})
t.Run("number maxConcurrentShardRequests", func(t *testing.T) {
server := mockElasticsearchServer()
defer server.Close()
dsInfo := datasourceInfo{
TimeField: "@timestamp",
MaxConcurrentShardRequests: 10,
@@ -155,7 +115,6 @@ func TestNewInstanceSettings(t *testing.T) {
require.NoError(t, err)
dsSettings := backend.DataSourceInstanceSettings{
URL: server.URL,
JSONData: json.RawMessage(settingsJSON),
}
@@ -165,9 +124,6 @@ func TestNewInstanceSettings(t *testing.T) {
})
t.Run("zero maxConcurrentShardRequests", func(t *testing.T) {
server := mockElasticsearchServer()
defer server.Close()
dsInfo := datasourceInfo{
TimeField: "@timestamp",
MaxConcurrentShardRequests: 0,
@@ -176,7 +132,6 @@ func TestNewInstanceSettings(t *testing.T) {
require.NoError(t, err)
dsSettings := backend.DataSourceInstanceSettings{
URL: server.URL,
JSONData: json.RawMessage(settingsJSON),
}
@@ -186,9 +141,6 @@ func TestNewInstanceSettings(t *testing.T) {
})
t.Run("negative maxConcurrentShardRequests", func(t *testing.T) {
server := mockElasticsearchServer()
defer server.Close()
dsInfo := datasourceInfo{
TimeField: "@timestamp",
MaxConcurrentShardRequests: -10,
@@ -197,7 +149,6 @@ func TestNewInstanceSettings(t *testing.T) {
require.NoError(t, err)
dsSettings := backend.DataSourceInstanceSettings{
URL: server.URL,
JSONData: json.RawMessage(settingsJSON),
}
@@ -207,9 +158,6 @@ func TestNewInstanceSettings(t *testing.T) {
})
t.Run("float maxConcurrentShardRequests", func(t *testing.T) {
server := mockElasticsearchServer()
defer server.Close()
dsInfo := datasourceInfo{
TimeField: "@timestamp",
MaxConcurrentShardRequests: 10.5,
@@ -218,7 +166,6 @@ func TestNewInstanceSettings(t *testing.T) {
require.NoError(t, err)
dsSettings := backend.DataSourceInstanceSettings{
URL: server.URL,
JSONData: json.RawMessage(settingsJSON),
}
@@ -228,9 +175,6 @@ func TestNewInstanceSettings(t *testing.T) {
})
t.Run("invalid maxConcurrentShardRequests", func(t *testing.T) {
server := mockElasticsearchServer()
defer server.Close()
dsInfo := datasourceInfo{
TimeField: "@timestamp",
MaxConcurrentShardRequests: "invalid",
@@ -239,7 +183,6 @@ func TestNewInstanceSettings(t *testing.T) {
require.NoError(t, err)
dsSettings := backend.DataSourceInstanceSettings{
URL: server.URL,
JSONData: json.RawMessage(settingsJSON),
}
+1 -8
View File
@@ -28,6 +28,7 @@ func (s *Service) CheckHealth(ctx context.Context, req *backend.CheckHealthReque
Message: "Health check failed: Failed to get data source info",
}, nil
}
healthStatusUrl, err := url.Parse(ds.URL)
if err != nil {
logger.Error("Failed to parse data source URL", "error", err)
@@ -37,14 +38,6 @@ func (s *Service) CheckHealth(ctx context.Context, req *backend.CheckHealthReque
}, nil
}
// If the cluster is serverless, return a healthy result
if ds.ClusterInfo.IsServerless() {
return &backend.CheckHealthResult{
Status: backend.HealthStatusOk,
Message: "Elasticsearch Serverless data source is healthy.",
}, nil
}
// check that ES is healthy
healthStatusUrl.Path = path.Join(healthStatusUrl.Path, "_cluster/health")
healthStatusUrl.RawQuery = "wait_for_status=yellow"
@@ -9,7 +9,7 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/tsdb/elasticsearch/simplejson"
"github.com/grafana/grafana/pkg/components/simplejson"
)
// metricsResponseProcessor handles processing of metrics query responses
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"time"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/tsdb/elasticsearch/simplejson"
"github.com/grafana/grafana/pkg/components/simplejson"
)
// Query represents the time series query model of the datasource

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