grafana/pkg/registry/apis/query/register.go

300 lines
9.4 KiB
Go
Raw Normal View History

2024-02-01 02:36:51 +08:00
package query
import (
"context"
"encoding/json"
"github.com/prometheus/client_golang/prometheus"
2024-02-01 02:36:51 +08:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server"
common "k8s.io/kube-openapi/pkg/common"
"k8s.io/kube-openapi/pkg/spec3"
claims "github.com/grafana/authlib/types"
query "github.com/grafana/grafana/pkg/apis/query/v0alpha1"
"github.com/grafana/grafana/pkg/expr"
"github.com/grafana/grafana/pkg/expr/metrics"
2024-02-01 02:36:51 +08:00
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
2024-02-01 02:36:51 +08:00
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/registry/apis/query/client"
"github.com/grafana/grafana/pkg/registry/apis/query/clientapi"
"github.com/grafana/grafana/pkg/registry/apis/query/queryschema"
2024-02-01 02:36:51 +08:00
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/apiserver/builder"
2024-02-01 02:36:51 +08:00
"github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/datasources/service"
2024-02-01 02:36:51 +08:00
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
"github.com/grafana/grafana/pkg/setting"
2024-02-01 02:36:51 +08:00
)
var _ builder.APIGroupBuilder = (*QueryAPIBuilder)(nil)
2024-02-01 02:36:51 +08:00
type QueryAPIBuilder struct {
Query Service: Combine SSE handling in single tenant and multi tenant paths (#108041) * parse via sse I need to figure out how to handle the pipeline.execute with our own client. I think this is important for MT reasons, just like using our own cache (via legacy) is important. parsing is done though! * WIP nonsense * horrible code but i think it works * Add support for sql expressions config settings * Cleanup: - remove spew from nodes.go - uncomment out plugin context and use in single tenant flow - make code more readable and add comments * Cleanup: - create separate file for mt ds client builder - ensure error handling is the same for both expressions and regular queries - other cleanup * not working but good thoughts * WIP, vector not working for non sse * super hacky but i think vectors work now * delete delete delete * Comments for future ref * break out query handling and start test * add prom debugger * clean up: remove comments and commented out bits * fix query_test * add prom debugger * create table-driven tests with testsdata files * Fix test * Add test * go mod?? * idk * Remove comment * go enterprise issue maybe * Fix codeowners * Delete * Remove test data * Clean up * logger * Remove go changes hopefully * idk go man * sad * idk i ran go mod tidy and this is what it wants * Fix readme, with much help from adam * some linting and testing errors * lint * fix lint * fix lint register.go * another lint * address lint in test * fix dead code and linters for query_test * Go mod? * Struggling with go mod * Fix test * Fix another test * Revert headers change * Its difficult to test this in OSS as it depends on functionality defined in enterprise, let's bring these tests back in some form in enterprise * Fix codeowners --------- Co-authored-by: Adam Simpson <adam@adamsimpson.net>
2025-07-18 05:22:55 +08:00
log log.Logger
concurrentQueryLimit int
features featuremgmt.FeatureToggles
2024-02-01 02:36:51 +08:00
authorizer authorizer.Authorizer
Query Service: Combine SSE handling in single tenant and multi tenant paths (#108041) * parse via sse I need to figure out how to handle the pipeline.execute with our own client. I think this is important for MT reasons, just like using our own cache (via legacy) is important. parsing is done though! * WIP nonsense * horrible code but i think it works * Add support for sql expressions config settings * Cleanup: - remove spew from nodes.go - uncomment out plugin context and use in single tenant flow - make code more readable and add comments * Cleanup: - create separate file for mt ds client builder - ensure error handling is the same for both expressions and regular queries - other cleanup * not working but good thoughts * WIP, vector not working for non sse * super hacky but i think vectors work now * delete delete delete * Comments for future ref * break out query handling and start test * add prom debugger * clean up: remove comments and commented out bits * fix query_test * add prom debugger * create table-driven tests with testsdata files * Fix test * Add test * go mod?? * idk * Remove comment * go enterprise issue maybe * Fix codeowners * Delete * Remove test data * Clean up * logger * Remove go changes hopefully * idk go man * sad * idk i ran go mod tidy and this is what it wants * Fix readme, with much help from adam * some linting and testing errors * lint * fix lint * fix lint register.go * another lint * address lint in test * fix dead code and linters for query_test * Go mod? * Struggling with go mod * Fix test * Fix another test * Revert headers change * Its difficult to test this in OSS as it depends on functionality defined in enterprise, let's bring these tests back in some form in enterprise * Fix codeowners --------- Co-authored-by: Adam Simpson <adam@adamsimpson.net>
2025-07-18 05:22:55 +08:00
tracer tracing.Tracer
metrics *metrics.ExprMetrics
instanceProvider clientapi.InstanceProvider
Query Service: Combine SSE handling in single tenant and multi tenant paths (#108041) * parse via sse I need to figure out how to handle the pipeline.execute with our own client. I think this is important for MT reasons, just like using our own cache (via legacy) is important. parsing is done though! * WIP nonsense * horrible code but i think it works * Add support for sql expressions config settings * Cleanup: - remove spew from nodes.go - uncomment out plugin context and use in single tenant flow - make code more readable and add comments * Cleanup: - create separate file for mt ds client builder - ensure error handling is the same for both expressions and regular queries - other cleanup * not working but good thoughts * WIP, vector not working for non sse * super hacky but i think vectors work now * delete delete delete * Comments for future ref * break out query handling and start test * add prom debugger * clean up: remove comments and commented out bits * fix query_test * add prom debugger * create table-driven tests with testsdata files * Fix test * Add test * go mod?? * idk * Remove comment * go enterprise issue maybe * Fix codeowners * Delete * Remove test data * Clean up * logger * Remove go changes hopefully * idk go man * sad * idk i ran go mod tidy and this is what it wants * Fix readme, with much help from adam * some linting and testing errors * lint * fix lint * fix lint register.go * another lint * address lint in test * fix dead code and linters for query_test * Go mod? * Struggling with go mod * Fix test * Fix another test * Revert headers change * Its difficult to test this in OSS as it depends on functionality defined in enterprise, let's bring these tests back in some form in enterprise * Fix codeowners --------- Co-authored-by: Adam Simpson <adam@adamsimpson.net>
2025-07-18 05:22:55 +08:00
registry query.DataSourceApiServerRegistry
converter *expr.ResultConverter
queryTypes *query.QueryTypeDefinitionList
legacyDatasourceLookup service.LegacyDataSourceLookup
connections DataSourceConnectionProvider
2024-02-01 02:36:51 +08:00
}
Query Service: Combine SSE handling in single tenant and multi tenant paths (#108041) * parse via sse I need to figure out how to handle the pipeline.execute with our own client. I think this is important for MT reasons, just like using our own cache (via legacy) is important. parsing is done though! * WIP nonsense * horrible code but i think it works * Add support for sql expressions config settings * Cleanup: - remove spew from nodes.go - uncomment out plugin context and use in single tenant flow - make code more readable and add comments * Cleanup: - create separate file for mt ds client builder - ensure error handling is the same for both expressions and regular queries - other cleanup * not working but good thoughts * WIP, vector not working for non sse * super hacky but i think vectors work now * delete delete delete * Comments for future ref * break out query handling and start test * add prom debugger * clean up: remove comments and commented out bits * fix query_test * add prom debugger * create table-driven tests with testsdata files * Fix test * Add test * go mod?? * idk * Remove comment * go enterprise issue maybe * Fix codeowners * Delete * Remove test data * Clean up * logger * Remove go changes hopefully * idk go man * sad * idk i ran go mod tidy and this is what it wants * Fix readme, with much help from adam * some linting and testing errors * lint * fix lint * fix lint register.go * another lint * address lint in test * fix dead code and linters for query_test * Go mod? * Struggling with go mod * Fix test * Fix another test * Revert headers change * Its difficult to test this in OSS as it depends on functionality defined in enterprise, let's bring these tests back in some form in enterprise * Fix codeowners --------- Co-authored-by: Adam Simpson <adam@adamsimpson.net>
2025-07-18 05:22:55 +08:00
func NewQueryAPIBuilder(
features featuremgmt.FeatureToggles,
instanceProvider clientapi.InstanceProvider,
ar authorizer.Authorizer,
registry query.DataSourceApiServerRegistry,
registerer prometheus.Registerer,
tracer tracing.Tracer,
Query Service: Combine SSE handling in single tenant and multi tenant paths (#108041) * parse via sse I need to figure out how to handle the pipeline.execute with our own client. I think this is important for MT reasons, just like using our own cache (via legacy) is important. parsing is done though! * WIP nonsense * horrible code but i think it works * Add support for sql expressions config settings * Cleanup: - remove spew from nodes.go - uncomment out plugin context and use in single tenant flow - make code more readable and add comments * Cleanup: - create separate file for mt ds client builder - ensure error handling is the same for both expressions and regular queries - other cleanup * not working but good thoughts * WIP, vector not working for non sse * super hacky but i think vectors work now * delete delete delete * Comments for future ref * break out query handling and start test * add prom debugger * clean up: remove comments and commented out bits * fix query_test * add prom debugger * create table-driven tests with testsdata files * Fix test * Add test * go mod?? * idk * Remove comment * go enterprise issue maybe * Fix codeowners * Delete * Remove test data * Clean up * logger * Remove go changes hopefully * idk go man * sad * idk i ran go mod tidy and this is what it wants * Fix readme, with much help from adam * some linting and testing errors * lint * fix lint * fix lint register.go * another lint * address lint in test * fix dead code and linters for query_test * Go mod? * Struggling with go mod * Fix test * Fix another test * Revert headers change * Its difficult to test this in OSS as it depends on functionality defined in enterprise, let's bring these tests back in some form in enterprise * Fix codeowners --------- Co-authored-by: Adam Simpson <adam@adamsimpson.net>
2025-07-18 05:22:55 +08:00
legacyDatasourceLookup service.LegacyDataSourceLookup,
connections DataSourceConnectionProvider,
) (*QueryAPIBuilder, error) {
// Include well typed query definitions
var queryTypes *query.QueryTypeDefinitionList
if features.IsEnabledGlobally(featuremgmt.FlagDatasourceQueryTypes) {
// Read the expression query definitions
raw, err := expr.QueryTypeDefinitionListJSON()
if err != nil {
return nil, err
}
queryTypes = &query.QueryTypeDefinitionList{}
err = json.Unmarshal(raw, queryTypes)
if err != nil {
return nil, err
}
}
2024-02-01 02:36:51 +08:00
return &QueryAPIBuilder{
concurrentQueryLimit: 4,
2024-02-01 02:36:51 +08:00
log: log.New("query_apiserver"),
instanceProvider: instanceProvider,
authorizer: ar,
2024-02-01 02:36:51 +08:00
registry: registry,
metrics: metrics.NewQueryServiceExpressionsMetrics(registerer),
tracer: tracer,
features: features,
queryTypes: queryTypes,
connections: connections,
converter: &expr.ResultConverter{
Features: features,
Tracer: tracer,
},
Query Service: Combine SSE handling in single tenant and multi tenant paths (#108041) * parse via sse I need to figure out how to handle the pipeline.execute with our own client. I think this is important for MT reasons, just like using our own cache (via legacy) is important. parsing is done though! * WIP nonsense * horrible code but i think it works * Add support for sql expressions config settings * Cleanup: - remove spew from nodes.go - uncomment out plugin context and use in single tenant flow - make code more readable and add comments * Cleanup: - create separate file for mt ds client builder - ensure error handling is the same for both expressions and regular queries - other cleanup * not working but good thoughts * WIP, vector not working for non sse * super hacky but i think vectors work now * delete delete delete * Comments for future ref * break out query handling and start test * add prom debugger * clean up: remove comments and commented out bits * fix query_test * add prom debugger * create table-driven tests with testsdata files * Fix test * Add test * go mod?? * idk * Remove comment * go enterprise issue maybe * Fix codeowners * Delete * Remove test data * Clean up * logger * Remove go changes hopefully * idk go man * sad * idk i ran go mod tidy and this is what it wants * Fix readme, with much help from adam * some linting and testing errors * lint * fix lint * fix lint register.go * another lint * address lint in test * fix dead code and linters for query_test * Go mod? * Struggling with go mod * Fix test * Fix another test * Revert headers change * Its difficult to test this in OSS as it depends on functionality defined in enterprise, let's bring these tests back in some form in enterprise * Fix codeowners --------- Co-authored-by: Adam Simpson <adam@adamsimpson.net>
2025-07-18 05:22:55 +08:00
legacyDatasourceLookup: legacyDatasourceLookup,
}, nil
2024-02-01 02:36:51 +08:00
}
func RegisterAPIService(
cfg *setting.Cfg,
features featuremgmt.FeatureToggles,
apiregistration builder.APIRegistrar,
2024-02-01 02:36:51 +08:00
dataSourcesService datasources.DataSourceService,
pluginStore pluginstore.Store,
accessControl accesscontrol.AccessControl,
pluginClient plugins.Client,
pCtxProvider *plugincontext.Provider,
registerer prometheus.Registerer,
tracer tracing.Tracer,
Query Service: Combine SSE handling in single tenant and multi tenant paths (#108041) * parse via sse I need to figure out how to handle the pipeline.execute with our own client. I think this is important for MT reasons, just like using our own cache (via legacy) is important. parsing is done though! * WIP nonsense * horrible code but i think it works * Add support for sql expressions config settings * Cleanup: - remove spew from nodes.go - uncomment out plugin context and use in single tenant flow - make code more readable and add comments * Cleanup: - create separate file for mt ds client builder - ensure error handling is the same for both expressions and regular queries - other cleanup * not working but good thoughts * WIP, vector not working for non sse * super hacky but i think vectors work now * delete delete delete * Comments for future ref * break out query handling and start test * add prom debugger * clean up: remove comments and commented out bits * fix query_test * add prom debugger * create table-driven tests with testsdata files * Fix test * Add test * go mod?? * idk * Remove comment * go enterprise issue maybe * Fix codeowners * Delete * Remove test data * Clean up * logger * Remove go changes hopefully * idk go man * sad * idk i ran go mod tidy and this is what it wants * Fix readme, with much help from adam * some linting and testing errors * lint * fix lint * fix lint register.go * another lint * address lint in test * fix dead code and linters for query_test * Go mod? * Struggling with go mod * Fix test * Fix another test * Revert headers change * Its difficult to test this in OSS as it depends on functionality defined in enterprise, let's bring these tests back in some form in enterprise * Fix codeowners --------- Co-authored-by: Adam Simpson <adam@adamsimpson.net>
2025-07-18 05:22:55 +08:00
legacyDatasourceLookup service.LegacyDataSourceLookup,
exprService *expr.Service,
) (*QueryAPIBuilder, error) {
if !featuremgmt.AnyEnabled(features,
featuremgmt.FlagQueryService,
featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs) {
return nil, nil // skip registration unless explicitly added (or all experimental are added)
2024-02-01 02:36:51 +08:00
}
ar := authorizer.AuthorizerFunc(
func(ctx context.Context, attr authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) {
// we only verify that we have a valid user.
// the "real" check will happen when the specific
// data sources are loaded.
_, ok := claims.AuthInfoFrom(ctx)
if !ok {
return authorizer.DecisionDeny, "valid user is required", nil
}
return authorizer.DecisionAllow, "", nil
})
reg := client.NewDataSourceRegistryFromStore(pluginStore, dataSourcesService)
builder, err := NewQueryAPIBuilder(
2024-02-01 02:36:51 +08:00
features,
client.NewSingleTenantInstanceProvider(cfg, features, pluginClient, pCtxProvider, accessControl),
ar,
client.NewDataSourceRegistryFromStore(pluginStore, dataSourcesService),
Query Service: Combine SSE handling in single tenant and multi tenant paths (#108041) * parse via sse I need to figure out how to handle the pipeline.execute with our own client. I think this is important for MT reasons, just like using our own cache (via legacy) is important. parsing is done though! * WIP nonsense * horrible code but i think it works * Add support for sql expressions config settings * Cleanup: - remove spew from nodes.go - uncomment out plugin context and use in single tenant flow - make code more readable and add comments * Cleanup: - create separate file for mt ds client builder - ensure error handling is the same for both expressions and regular queries - other cleanup * not working but good thoughts * WIP, vector not working for non sse * super hacky but i think vectors work now * delete delete delete * Comments for future ref * break out query handling and start test * add prom debugger * clean up: remove comments and commented out bits * fix query_test * add prom debugger * create table-driven tests with testsdata files * Fix test * Add test * go mod?? * idk * Remove comment * go enterprise issue maybe * Fix codeowners * Delete * Remove test data * Clean up * logger * Remove go changes hopefully * idk go man * sad * idk i ran go mod tidy and this is what it wants * Fix readme, with much help from adam * some linting and testing errors * lint * fix lint * fix lint register.go * another lint * address lint in test * fix dead code and linters for query_test * Go mod? * Struggling with go mod * Fix test * Fix another test * Revert headers change * Its difficult to test this in OSS as it depends on functionality defined in enterprise, let's bring these tests back in some form in enterprise * Fix codeowners --------- Co-authored-by: Adam Simpson <adam@adamsimpson.net>
2025-07-18 05:22:55 +08:00
registerer,
tracer,
legacyDatasourceLookup,
&connectionsProvider{dsService: dataSourcesService, registry: reg},
2024-02-01 02:36:51 +08:00
)
apiregistration.RegisterAPI(builder)
return builder, err
2024-02-01 02:36:51 +08:00
}
func (b *QueryAPIBuilder) GetGroupVersion() schema.GroupVersion {
return query.SchemeGroupVersion
2024-02-01 02:36:51 +08:00
}
func addKnownTypes(scheme *runtime.Scheme, gv schema.GroupVersion) {
scheme.AddKnownTypes(gv,
&query.DataSourceApiServer{},
&query.DataSourceApiServerList{},
&query.DataSourceConnection{},
&query.DataSourceConnectionList{},
&query.QueryDataRequest{},
&query.QueryDataResponse{},
&query.QueryTypeDefinition{},
&query.QueryTypeDefinitionList{},
2024-02-01 02:36:51 +08:00
)
}
func (b *QueryAPIBuilder) InstallSchema(scheme *runtime.Scheme) error {
addKnownTypes(scheme, query.SchemeGroupVersion)
metav1.AddToGroupVersion(scheme, query.SchemeGroupVersion)
return scheme.SetVersionPriority(query.SchemeGroupVersion)
2024-02-01 02:36:51 +08:00
}
2025-06-21 05:37:17 +08:00
func (b *QueryAPIBuilder) AllowedV0Alpha1Resources() []string {
return []string{builder.AllResourcesAllowed}
}
func (b *QueryAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver.APIGroupInfo, _ builder.APIGroupOptions) error {
gv := query.SchemeGroupVersion
2024-02-01 02:36:51 +08:00
storage := map[string]rest.Storage{}
// Get a list of all datasource instances
if b.features.IsEnabledGlobally(featuremgmt.FlagQueryServiceWithConnections) {
// Eventually this would be backed either by search or reconciler pattern
storage[query.ConnectionResourceInfo.StoragePath()] = &connectionAccess{
connections: b.connections,
}
}
plugins := newPluginsStorage(b.registry)
2024-02-01 02:36:51 +08:00
storage[plugins.resourceInfo.StoragePath()] = plugins
if !b.features.IsEnabledGlobally(featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs) {
// The plugin registry is still experimental, and not yet accurate
// For standard k8s api discovery to work, at least one resource must be registered
// While this feature is under development, we can return an empty list for non-dev instances
plugins.returnEmptyList = true
}
2024-02-01 02:36:51 +08:00
// The query endpoint -- NOTE, this uses a rewrite hack to allow requests without a name parameter
storage["query"] = newQueryREST(b)
// Register the expressions query schemas
err := queryschema.RegisterQueryTypes(b.queryTypes, storage)
2024-02-01 02:36:51 +08:00
apiGroupInfo.VersionedResourcesStorageMap[gv.Version] = storage
return err
2024-02-01 02:36:51 +08:00
}
func (b *QueryAPIBuilder) GetOpenAPIDefinitions() common.GetOpenAPIDefinitions {
return query.GetOpenAPIDefinitions
2024-02-01 02:36:51 +08:00
}
func (b *QueryAPIBuilder) GetAuthorizer() authorizer.Authorizer {
return b.authorizer
2024-02-01 02:36:51 +08:00
}
func (b *QueryAPIBuilder) PostProcessOpenAPI(oas *spec3.OpenAPI) (*spec3.OpenAPI, error) {
// The plugin description
oas.Info.Description = "Query service"
// The root api URL
root := "/apis/" + b.GetGroupVersion().String() + "/"
// Add queries to the request properties
err := queryschema.AddQueriesToOpenAPI(queryschema.OASQueryOptions{
Swagger: oas,
PluginJSON: &plugins.JSONData{
ID: expr.DatasourceType, // Not really a plugin, but identified the same way
},
QueryTypes: b.queryTypes,
Root: root,
QueryPath: "namespaces/{namespace}/query/{name}",
QueryDescription: "Query any datasources (with expressions)",
// An explicit set of examples (otherwise we iterate the query type examples)
QueryExamples: map[string]*spec3.Example{
"A": {
ExampleProps: spec3.ExampleProps{
Summary: "Random walk (testdata)",
Description: "Use testdata to execute a random walk query",
Value: `{
"queries": [
{
"refId": "A",
"scenarioId": "random_walk_table",
"seriesCount": 1,
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"intervalMs": 60000,
"maxDataPoints": 20
}
],
"from": "now-6h",
"to": "now"
}`,
},
},
"B": {
ExampleProps: spec3.ExampleProps{
Summary: "With deprecated datasource name",
Description: "Includes an old style string for datasource reference",
Value: `{
"queries": [
{
"refId": "A",
"datasource": {
"type": "grafana-googlesheets-datasource",
"uid": "b1808c48-9fc9-4045-82d7-081781f8a553"
},
"cacheDurationSeconds": 300,
"spreadsheet": "spreadsheetID",
"datasourceId": 4,
"intervalMs": 30000,
"maxDataPoints": 794
},
{
"refId": "Z",
"datasource": "old",
"maxDataPoints": 10,
"timeRange": {
"from": "100",
"to": "200"
}
}
],
"from": "now-6h",
"to": "now"
}`,
},
},
},
})
if err != nil {
return oas, nil
}
return oas, nil
}