K8s/Testdata: Expose testdata in standalone apiserver (#80248)
This commit is contained in:
@@ -2,7 +2,7 @@ Experimental!
|
||||
|
||||
This is exploring how to expose any datasource as a k8s aggregated API server.
|
||||
|
||||
Unlike the other services, this will register other plugins as:
|
||||
Unlike the other services, this will register datasources as:
|
||||
|
||||
> {plugin}.datasource.grafana.app
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apis"
|
||||
@@ -57,32 +58,38 @@ func (s *connectionAccess) ConvertToTable(ctx context.Context, object runtime.Ob
|
||||
}
|
||||
|
||||
func (s *connectionAccess) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
|
||||
ns := request.NamespaceValue(ctx)
|
||||
ds, err := s.builder.getDataSource(ctx, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.asConnection(ds), nil
|
||||
return s.asConnection(ds, ns), nil
|
||||
}
|
||||
|
||||
func (s *connectionAccess) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
|
||||
ns := request.NamespaceValue(ctx)
|
||||
if ns == "" {
|
||||
// require a namespace so we do not need to support reverse mappings (yet)
|
||||
return nil, fmt.Errorf("missing namespace in request URL")
|
||||
}
|
||||
result := &v0alpha1.DataSourceConnectionList{
|
||||
Items: []v0alpha1.DataSourceConnection{},
|
||||
}
|
||||
vals, err := s.builder.getDataSources(ctx)
|
||||
if err == nil {
|
||||
for _, ds := range vals {
|
||||
result.Items = append(result.Items, *s.asConnection(ds))
|
||||
result.Items = append(result.Items, *s.asConnection(ds, ns))
|
||||
}
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *connectionAccess) asConnection(ds *datasources.DataSource) *v0alpha1.DataSourceConnection {
|
||||
func (s *connectionAccess) asConnection(ds *datasources.DataSource, ns string) *v0alpha1.DataSourceConnection {
|
||||
v := &v0alpha1.DataSourceConnection{
|
||||
TypeMeta: s.resourceInfo.TypeMeta(),
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: ds.UID,
|
||||
Namespace: s.builder.namespacer(ds.OrgID),
|
||||
Namespace: ns,
|
||||
CreationTimestamp: metav1.NewTime(ds.Created),
|
||||
ResourceVersion: fmt.Sprintf("%d", ds.Updated.UnixMilli()),
|
||||
},
|
||||
|
||||
@@ -37,12 +37,11 @@ var _ grafanaapiserver.APIGroupBuilder = (*DataSourceAPIBuilder)(nil)
|
||||
type DataSourceAPIBuilder struct {
|
||||
connectionResourceInfo apis.ResourceInfo
|
||||
|
||||
plugin pluginstore.Plugin
|
||||
plugin plugins.JSONData
|
||||
client plugins.Client
|
||||
dsService datasources.DataSourceService
|
||||
dsCache datasources.CacheService
|
||||
accessControl accesscontrol.AccessControl
|
||||
namespacer request.NamespaceMapper
|
||||
}
|
||||
|
||||
func RegisterAPIService(
|
||||
@@ -67,13 +66,12 @@ func RegisterAPIService(
|
||||
"grafana-testdata-datasource",
|
||||
}
|
||||
|
||||
namespacer := request.GetNamespaceMapper(cfg)
|
||||
for _, ds := range all {
|
||||
if !slices.Contains(ids, ds.ID) {
|
||||
continue // skip this one
|
||||
}
|
||||
|
||||
builder, err = NewDataSourceAPIBuilder(ds, pluginClient, dsService, dsCache, accessControl, namespacer)
|
||||
builder, err = NewDataSourceAPIBuilder(ds.JSONData, pluginClient, dsService, dsCache, accessControl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -83,12 +81,11 @@ func RegisterAPIService(
|
||||
}
|
||||
|
||||
func NewDataSourceAPIBuilder(
|
||||
plugin pluginstore.Plugin,
|
||||
plugin plugins.JSONData,
|
||||
client plugins.Client,
|
||||
dsService datasources.DataSourceService,
|
||||
dsCache datasources.CacheService,
|
||||
accessControl accesscontrol.AccessControl,
|
||||
namespacer request.NamespaceMapper) (*DataSourceAPIBuilder, error) {
|
||||
accessControl accesscontrol.AccessControl) (*DataSourceAPIBuilder, error) {
|
||||
group, err := getDatasourceGroupNameFromPluginID(plugin.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -100,7 +97,6 @@ func NewDataSourceAPIBuilder(
|
||||
dsService: dsService,
|
||||
dsCache: dsCache,
|
||||
accessControl: accessControl,
|
||||
namespacer: namespacer,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package datasource
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
fakeDatasources "github.com/grafana/grafana/pkg/services/datasources/fakes"
|
||||
testdatasource "github.com/grafana/grafana/pkg/tsdb/grafana-testdata-datasource"
|
||||
)
|
||||
|
||||
// This is a helper function to create a new datasource API server for a group
|
||||
// This currently has no dependencies and only works for testdata. In future iterations
|
||||
// this will include here (or elsewhere) versions that can load config from HG api or
|
||||
// the remote SQL directly
|
||||
func NewStandaloneDatasource(group string) (*DataSourceAPIBuilder, error) {
|
||||
if group != "testdata.datasource.grafana.app" {
|
||||
return nil, fmt.Errorf("only testadata is currently supported")
|
||||
}
|
||||
|
||||
orgId := int64(1)
|
||||
pluginId := "grafana-testdata-datasource"
|
||||
now := time.Now()
|
||||
dss := []*datasources.DataSource{
|
||||
{
|
||||
OrgID: orgId, // default -- used in the list command
|
||||
Type: pluginId,
|
||||
UID: "builtin", // fake for now
|
||||
Created: now,
|
||||
Updated: now,
|
||||
Name: "Testdata (builtin)",
|
||||
},
|
||||
{
|
||||
OrgID: orgId, // default -- used in the list command
|
||||
Type: pluginId,
|
||||
UID: "PD8C576611E62080A", // match the gdev version
|
||||
Created: now,
|
||||
Updated: now,
|
||||
Name: "gdev-testdata",
|
||||
},
|
||||
}
|
||||
return NewDataSourceAPIBuilder(
|
||||
plugins.JSONData{ID: pluginId}, testdatasource.ProvideService(),
|
||||
&fakeDatasources.FakeDataSourceService{DataSources: dss},
|
||||
&fakeDatasources.FakeCacheService{DataSources: dss},
|
||||
// Always allow... but currently not called in standalone!
|
||||
&actest.FakeAccessControl{ExpectedEvaluate: true},
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user