mirror of https://github.com/grafana/grafana.git
Make it safe to run new config CRUD APIs in DS API server
Using `configCrudUseNewApis` passed through for DS API servers from the `grafana-enterprise` codebase.
This commit is contained in:
parent
c3f34efb41
commit
b7c73a9bfc
|
@ -0,0 +1,59 @@
|
||||||
|
package datasource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apiserver/pkg/registry/rest"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ rest.Scoper = (*connectionAccess)(nil)
|
||||||
|
_ rest.SingularNameProvider = (*connectionAccess)(nil)
|
||||||
|
_ rest.Getter = (*connectionAccess)(nil)
|
||||||
|
_ rest.Lister = (*connectionAccess)(nil)
|
||||||
|
_ rest.Storage = (*connectionAccess)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
type connectionAccess struct {
|
||||||
|
resourceInfo utils.ResourceInfo
|
||||||
|
tableConverter rest.TableConvertor
|
||||||
|
datasources PluginDatasourceProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *connectionAccess) New() runtime.Object {
|
||||||
|
return s.resourceInfo.NewFunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *connectionAccess) Destroy() {}
|
||||||
|
|
||||||
|
func (s *connectionAccess) NamespaceScoped() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *connectionAccess) GetSingularName() string {
|
||||||
|
return s.resourceInfo.GetSingularName()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *connectionAccess) ShortNames() []string {
|
||||||
|
return s.resourceInfo.GetShortNames()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *connectionAccess) NewList() runtime.Object {
|
||||||
|
return s.resourceInfo.NewListFunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *connectionAccess) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
|
||||||
|
return s.tableConverter.ConvertToTable(ctx, object, tableOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *connectionAccess) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
|
||||||
|
return s.datasources.GetDataSource(ctx, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *connectionAccess) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
|
||||||
|
return s.datasources.ListDataSources(ctx)
|
||||||
|
}
|
|
@ -3,8 +3,10 @@ package datasource
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"maps"
|
"maps"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
@ -24,11 +26,13 @@ import (
|
||||||
"github.com/grafana/grafana/pkg/configprovider"
|
"github.com/grafana/grafana/pkg/configprovider"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
|
"github.com/grafana/grafana/pkg/plugins/manager/sources"
|
||||||
"github.com/grafana/grafana/pkg/promlib/models"
|
"github.com/grafana/grafana/pkg/promlib/models"
|
||||||
"github.com/grafana/grafana/pkg/registry/apis/query/queryschema"
|
"github.com/grafana/grafana/pkg/registry/apis/query/queryschema"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
"github.com/grafana/grafana/pkg/services/apiserver/builder"
|
"github.com/grafana/grafana/pkg/services/apiserver/builder"
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/grafana-testdata-datasource/kinds"
|
"github.com/grafana/grafana/pkg/tsdb/grafana-testdata-datasource/kinds"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -40,13 +44,14 @@ var (
|
||||||
type DataSourceAPIBuilder struct {
|
type DataSourceAPIBuilder struct {
|
||||||
datasourceResourceInfo utils.ResourceInfo
|
datasourceResourceInfo utils.ResourceInfo
|
||||||
|
|
||||||
pluginJSON plugins.JSONData
|
pluginJSON plugins.JSONData
|
||||||
client PluginClient // will only ever be called with the same plugin id!
|
client PluginClient // will only ever be called with the same plugin id!
|
||||||
datasources PluginDatasourceProvider
|
datasources PluginDatasourceProvider
|
||||||
contextProvider PluginContextWrapper
|
contextProvider PluginContextWrapper
|
||||||
accessControl accesscontrol.AccessControl
|
accessControl accesscontrol.AccessControl
|
||||||
queryTypes *queryV0.QueryTypeDefinitionList
|
queryTypes *queryV0.QueryTypeDefinitionList
|
||||||
log log.Logger
|
log log.Logger
|
||||||
|
configCrudUseNewApis bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func RegisterAPIService(
|
func RegisterAPIService(
|
||||||
|
@ -109,16 +114,12 @@ func RegisterAPIService(
|
||||||
contextProvider,
|
contextProvider,
|
||||||
accessControl,
|
accessControl,
|
||||||
features.IsEnabledGlobally(featuremgmt.FlagDatasourceQueryTypes),
|
features.IsEnabledGlobally(featuremgmt.FlagDatasourceQueryTypes),
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: load the schema provider from a static manifest
|
|
||||||
// if ds.ID == "grafana-testdata-datasource" {
|
|
||||||
// builder.schemaProvider = hardcoded.TestdataOpenAPIExtension
|
|
||||||
// }
|
|
||||||
|
|
||||||
apiRegistrar.RegisterAPI(builder)
|
apiRegistrar.RegisterAPI(builder)
|
||||||
}
|
}
|
||||||
return builder, nil // only used for wire
|
return builder, nil // only used for wire
|
||||||
|
@ -140,6 +141,7 @@ func NewDataSourceAPIBuilder(
|
||||||
contextProvider PluginContextWrapper,
|
contextProvider PluginContextWrapper,
|
||||||
accessControl accesscontrol.AccessControl,
|
accessControl accesscontrol.AccessControl,
|
||||||
loadQueryTypes bool,
|
loadQueryTypes bool,
|
||||||
|
configCrudUseNewApis bool,
|
||||||
) (*DataSourceAPIBuilder, error) {
|
) (*DataSourceAPIBuilder, error) {
|
||||||
group, err := plugins.GetDatasourceGroupNameFromPluginID(plugin.ID)
|
group, err := plugins.GetDatasourceGroupNameFromPluginID(plugin.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -154,6 +156,7 @@ func NewDataSourceAPIBuilder(
|
||||||
contextProvider: contextProvider,
|
contextProvider: contextProvider,
|
||||||
accessControl: accessControl,
|
accessControl: accessControl,
|
||||||
log: log.New("grafana-apiserver.datasource"),
|
log: log.New("grafana-apiserver.datasource"),
|
||||||
|
configCrudUseNewApis: configCrudUseNewApis,
|
||||||
}
|
}
|
||||||
if loadQueryTypes {
|
if loadQueryTypes {
|
||||||
// In the future, this will somehow come from the plugin
|
// In the future, this will somehow come from the plugin
|
||||||
|
@ -232,19 +235,6 @@ func (b *DataSourceAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver
|
||||||
|
|
||||||
// Register the raw datasource connection
|
// Register the raw datasource connection
|
||||||
ds := b.datasourceResourceInfo
|
ds := b.datasourceResourceInfo
|
||||||
legacyStore := &legacyStorage{
|
|
||||||
datasources: b.datasources,
|
|
||||||
resourceInfo: &ds,
|
|
||||||
}
|
|
||||||
unified, err := grafanaregistry.NewRegistryStore(opts.Scheme, ds, opts.OptsGetter)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
storage[ds.StoragePath()], err = opts.DualWriteBuilder(ds.GroupResource(), legacyStore, unified)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
storage[ds.StoragePath("query")] = &subQueryREST{builder: b}
|
storage[ds.StoragePath("query")] = &subQueryREST{builder: b}
|
||||||
storage[ds.StoragePath("health")] = &subHealthREST{builder: b}
|
storage[ds.StoragePath("health")] = &subHealthREST{builder: b}
|
||||||
storage[ds.StoragePath("resource")] = &subResourceREST{builder: b}
|
storage[ds.StoragePath("resource")] = &subResourceREST{builder: b}
|
||||||
|
@ -255,13 +245,34 @@ func (b *DataSourceAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver
|
||||||
storage["connections"] = &noopREST{} // hidden from openapi
|
storage["connections"] = &noopREST{} // hidden from openapi
|
||||||
storage["connections/query"] = storage[ds.StoragePath("query")] // deprecated in openapi
|
storage["connections/query"] = storage[ds.StoragePath("query")] // deprecated in openapi
|
||||||
|
|
||||||
|
if b.configCrudUseNewApis {
|
||||||
|
legacyStore := &legacyStorage{
|
||||||
|
datasources: b.datasources,
|
||||||
|
resourceInfo: &ds,
|
||||||
|
}
|
||||||
|
unified, err := grafanaregistry.NewRegistryStore(opts.Scheme, ds, opts.OptsGetter)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
storage[ds.StoragePath()], err = opts.DualWriteBuilder(ds.GroupResource(), legacyStore, unified)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
storage[ds.StoragePath()] = &connectionAccess{
|
||||||
|
datasources: b.datasources,
|
||||||
|
resourceInfo: ds,
|
||||||
|
tableConverter: ds.TableConverter(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Frontend proxy
|
// Frontend proxy
|
||||||
if len(b.pluginJSON.Routes) > 0 {
|
if len(b.pluginJSON.Routes) > 0 {
|
||||||
storage[ds.StoragePath("proxy")] = &subProxyREST{pluginJSON: b.pluginJSON}
|
storage[ds.StoragePath("proxy")] = &subProxyREST{pluginJSON: b.pluginJSON}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register hardcoded query schemas
|
// Register hardcoded query schemas
|
||||||
err = queryschema.RegisterQueryTypes(b.queryTypes, storage)
|
err := queryschema.RegisterQueryTypes(b.queryTypes, storage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -287,3 +298,22 @@ func (b *DataSourceAPIBuilder) GetOpenAPIDefinitions() openapi.GetOpenAPIDefinit
|
||||||
return defs
|
return defs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getCorePlugins(cfg *setting.Cfg) ([]plugins.JSONData, error) {
|
||||||
|
coreDataSourcesPath := filepath.Join(cfg.StaticRootPath, "app", "plugins", "datasource")
|
||||||
|
coreDataSourcesSrc := sources.NewLocalSource(
|
||||||
|
plugins.ClassCore,
|
||||||
|
[]string{coreDataSourcesPath},
|
||||||
|
)
|
||||||
|
|
||||||
|
res, err := coreDataSourcesSrc.Discover(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("failed to load core data source plugins")
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginJSONs := make([]plugins.JSONData, 0, len(res))
|
||||||
|
for _, p := range res {
|
||||||
|
pluginJSONs = append(pluginJSONs, p.Primary.JSONData)
|
||||||
|
}
|
||||||
|
return pluginJSONs, nil
|
||||||
|
}
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"name": "cejobd88i85j4d",
|
"name": "cejobd88i85j4d",
|
||||||
"namespace": "org-0",
|
"namespace": "org-0",
|
||||||
"uid": "boDNh7zU3nXj46rOXIJI7r44qaxjs8yy9I9dOj1MyBoX",
|
"uid": "boDNh7zU3nXj46rOXIJI7r44qaxjs8yy9I9dOj1MyBoX"
|
||||||
"creationTimestamp": null
|
|
||||||
},
|
},
|
||||||
"spec": {
|
"spec": {
|
||||||
"jsonData": null,
|
"jsonData": null,
|
||||||
|
|
|
@ -3,8 +3,7 @@
|
||||||
"name": "cejobd88i85j4d",
|
"name": "cejobd88i85j4d",
|
||||||
"namespace": "org-0",
|
"namespace": "org-0",
|
||||||
"uid": "boDNh7zU3nXj46rOXIJI7r44qaxjs8yy9I9dOj1MyBoX",
|
"uid": "boDNh7zU3nXj46rOXIJI7r44qaxjs8yy9I9dOj1MyBoX",
|
||||||
"generation": 2,
|
"generation": 2
|
||||||
"creationTimestamp": null
|
|
||||||
},
|
},
|
||||||
"spec": {
|
"spec": {
|
||||||
"access": "proxy",
|
"access": "proxy",
|
||||||
|
|
Loading…
Reference in New Issue