From 471c4eb89d9755dc9bc9f828267cedd170380b4d Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Fri, 11 Jul 2025 12:50:47 -0700 Subject: [PATCH] fix unique name constraint --- pkg/apis/query/v0alpha1/datasource.go | 21 ++++++- .../query/v0alpha1/zz_generated.deepcopy.go | 17 ++++++ .../query/v0alpha1/zz_generated.openapi.go | 59 ++++++++++++++++--- ...enerated.openapi_violation_exceptions.list | 1 + pkg/registry/apis/query/connections.go | 33 +++++++---- pkg/registry/apis/query/register.go | 6 +- 6 files changed, 113 insertions(+), 24 deletions(-) diff --git a/pkg/apis/query/v0alpha1/datasource.go b/pkg/apis/query/v0alpha1/datasource.go index 7c2e92b99dc..e6f44007ad3 100644 --- a/pkg/apis/query/v0alpha1/datasource.go +++ b/pkg/apis/query/v0alpha1/datasource.go @@ -8,16 +8,31 @@ import ( ) // Connection to a datasource instance +// The connection name must be 'ds:{group}:{name}' // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type DataSourceConnection struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - // The display name + // The configured display name Title string `json:"title"` - // Optional description for the data source (does not exist yet) - Description string `json:"description,omitempty"` + // Reference to the kubernets datasource + Datasource DataSourceConnectionRef `json:"datasource"` +} + +type DataSourceConnectionRef struct { + Group string `json:"group"` + Version string `json:"version"` + Name string `json:"name"` + + // The plugin ID -- NOTE, this has a 1:1 mapping with apiGroup and should likely be removed + PluginID string `json:"pluginId"` +} + +// The valid connection name for a group + identifier +func DataSourceConnectionName(group, name string) string { + return "ds:" + group + ":" + name } // List of all datasource instances across all datasource apiservers diff --git a/pkg/apis/query/v0alpha1/zz_generated.deepcopy.go b/pkg/apis/query/v0alpha1/zz_generated.deepcopy.go index 4640449d433..38a7bd0b115 100644 --- a/pkg/apis/query/v0alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/query/v0alpha1/zz_generated.deepcopy.go @@ -80,6 +80,7 @@ func (in *DataSourceConnection) DeepCopyInto(out *DataSourceConnection) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Datasource = in.Datasource return } @@ -134,6 +135,22 @@ func (in *DataSourceConnectionList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DataSourceConnectionRef) DeepCopyInto(out *DataSourceConnectionRef) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataSourceConnectionRef. +func (in *DataSourceConnectionRef) DeepCopy() *DataSourceConnectionRef { + if in == nil { + return nil + } + out := new(DataSourceConnectionRef) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *QueryDataRequest) DeepCopyInto(out *QueryDataRequest) { *out = *in diff --git a/pkg/apis/query/v0alpha1/zz_generated.openapi.go b/pkg/apis/query/v0alpha1/zz_generated.openapi.go index 4bb1c34e1c0..55549b737c6 100644 --- a/pkg/apis/query/v0alpha1/zz_generated.openapi.go +++ b/pkg/apis/query/v0alpha1/zz_generated.openapi.go @@ -18,6 +18,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/grafana/grafana/pkg/apis/query/v0alpha1.DataSourceApiServerList": schema_pkg_apis_query_v0alpha1_DataSourceApiServerList(ref), "github.com/grafana/grafana/pkg/apis/query/v0alpha1.DataSourceConnection": schema_pkg_apis_query_v0alpha1_DataSourceConnection(ref), "github.com/grafana/grafana/pkg/apis/query/v0alpha1.DataSourceConnectionList": schema_pkg_apis_query_v0alpha1_DataSourceConnectionList(ref), + "github.com/grafana/grafana/pkg/apis/query/v0alpha1.DataSourceConnectionRef": schema_pkg_apis_query_v0alpha1_DataSourceConnectionRef(ref), "github.com/grafana/grafana/pkg/apis/query/v0alpha1.QueryDataRequest": schema_pkg_apis_query_v0alpha1_QueryDataRequest(ref), "github.com/grafana/grafana/pkg/apis/query/v0alpha1.QueryDataResponse": schema_pkg_apis_query_v0alpha1_QueryDataResponse(ref), "github.com/grafana/grafana/pkg/apis/query/v0alpha1.QueryTypeDefinition": schema_pkg_apis_query_v0alpha1_QueryTypeDefinition(ref), @@ -152,7 +153,7 @@ func schema_pkg_apis_query_v0alpha1_DataSourceConnection(ref common.ReferenceCal return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "Connection to a datasource instance", + Description: "Connection to a datasource instance The connection name must be 'ds:{group}:{name}'", Type: []string{"object"}, Properties: map[string]spec.Schema{ "kind": { @@ -177,25 +178,25 @@ func schema_pkg_apis_query_v0alpha1_DataSourceConnection(ref common.ReferenceCal }, "title": { SchemaProps: spec.SchemaProps{ - Description: "The display name", + Description: "The configured display name", Default: "", Type: []string{"string"}, Format: "", }, }, - "description": { + "datasource": { SchemaProps: spec.SchemaProps{ - Description: "Optional description for the data source (does not exist yet)", - Type: []string{"string"}, - Format: "", + Description: "Reference to the kubernets datasource", + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/query/v0alpha1.DataSourceConnectionRef"), }, }, }, - Required: []string{"title"}, + Required: []string{"title", "datasource"}, }, }, Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + "github.com/grafana/grafana/pkg/apis/query/v0alpha1.DataSourceConnectionRef", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, } } @@ -248,6 +249,48 @@ func schema_pkg_apis_query_v0alpha1_DataSourceConnectionList(ref common.Referenc } } +func schema_pkg_apis_query_v0alpha1_DataSourceConnectionRef(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "group": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "version": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "pluginId": { + SchemaProps: spec.SchemaProps{ + Description: "The plugin ID -- NOTE, this has a 1:1 mapping with apiGroup and should likely be removed", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"group", "version", "name", "pluginId"}, + }, + }, + } +} + func schema_pkg_apis_query_v0alpha1_QueryDataRequest(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/pkg/apis/query/v0alpha1/zz_generated.openapi_violation_exceptions.list b/pkg/apis/query/v0alpha1/zz_generated.openapi_violation_exceptions.list index 85c5a768907..9b86cd2ef7a 100644 --- a/pkg/apis/query/v0alpha1/zz_generated.openapi_violation_exceptions.list +++ b/pkg/apis/query/v0alpha1/zz_generated.openapi_violation_exceptions.list @@ -1 +1,2 @@ API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/query/v0alpha1,DataSourceApiServer,AliasIDs +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/query/v0alpha1,DataSourceConnectionRef,PluginID diff --git a/pkg/registry/apis/query/connections.go b/pkg/registry/apis/query/connections.go index 169ffd0d2d6..ad48a8094f7 100644 --- a/pkg/registry/apis/query/connections.go +++ b/pkg/registry/apis/query/connections.go @@ -79,6 +79,7 @@ func (s *connectionAccess) List(ctx context.Context, options *internalversion.Li type connectionsProvider struct { dsService datasources.DataSourceService + registry queryV0.DataSourceApiServerRegistry } func (q *connectionsProvider) GetConnection(ctx context.Context, namespace string, name string) (*queryV0.DataSourceConnection, error) { @@ -95,8 +96,7 @@ func (q *connectionsProvider) GetConnection(ctx context.Context, namespace strin } // TODO... access control? - - return asConnection(ds, namespace) + return q.asConnection(ds, namespace) } func (q *connectionsProvider) ListConnections(ctx context.Context, namespace string) (*queryV0.DataSourceConnectionList, error) { @@ -116,28 +116,41 @@ func (q *connectionsProvider) ListConnections(ctx context.Context, namespace str Items: []queryV0.DataSourceConnection{}, } for _, ds := range dss { - // TODO, access control! - v, _ := asConnection(ds, namespace) + // TODO, access control?! + v, _ := q.asConnection(ds, namespace) result.Items = append(result.Items, *v) } return result, nil } -func asConnection(ds *datasources.DataSource, ns string) (*queryV0.DataSourceConnection, error) { - v := &queryV0.DataSourceConnection{ +func (q *connectionsProvider) asConnection(ds *datasources.DataSource, ns string) (v *queryV0.DataSourceConnection, err error) { + gv, err := q.registry.GetDatasourceGroupVersion(ds.Type) + if err != nil { + return nil, err + } + + v = &queryV0.DataSourceConnection{ ObjectMeta: metav1.ObjectMeta{ - Name: ds.UID, + Name: queryV0.DataSourceConnectionName(gv.Group, ds.Name), Namespace: ns, CreationTimestamp: metav1.NewTime(ds.Created), ResourceVersion: fmt.Sprintf("%d", ds.Updated.UnixMilli()), Generation: int64(ds.Version), }, Title: ds.Name, + Datasource: queryV0.DataSourceConnectionRef{ + Group: gv.Group, + Version: gv.Version, + Name: ds.UID, + PluginID: ds.Type, + }, } v.UID = gapiutil.CalculateClusterWideUID(v) // indicates if the value changed on the server - meta, err := utils.MetaAccessor(v) - if err != nil { - meta.SetUpdatedTimestamp(&ds.Updated) + if !ds.Updated.IsZero() { + meta, err := utils.MetaAccessor(v) + if err != nil { + meta.SetUpdatedTimestamp(&ds.Updated) + } } return v, err } diff --git a/pkg/registry/apis/query/register.go b/pkg/registry/apis/query/register.go index 70497bd7209..9bb95bafbaf 100644 --- a/pkg/registry/apis/query/register.go +++ b/pkg/registry/apis/query/register.go @@ -128,14 +128,14 @@ func RegisterAPIService(features featuremgmt.FeatureToggles, return authorizer.DecisionAllow, "", nil }) + reg := client.NewDataSourceRegistryFromStore(pluginStore, dataSourcesService) builder, err := NewQueryAPIBuilder( features, &CommonDataSourceClientSupplier{ Client: client.NewQueryClientForPluginClient(pluginClient, pCtxProvider, accessControl), }, - ar, - client.NewDataSourceRegistryFromStore(pluginStore, dataSourcesService), - &connectionsProvider{dsService: dataSourcesService}, legacy, registerer, tracer, + ar, reg, + &connectionsProvider{dsService: dataSourcesService, registry: reg}, legacy, registerer, tracer, ) apiregistration.RegisterAPI(builder) return builder, err