From b0c93505807019d3248b2eb9d15048f7ba50744d Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Wed, 20 Aug 2025 10:38:52 +0300 Subject: [PATCH] unstructured spec --- pkg/apis/datasource/v0alpha1/config.go | 72 ---------------- pkg/apis/datasource/v0alpha1/register.go | 2 +- .../v0alpha1/zz_generated.deepcopy.go | 17 ---- .../v0alpha1/zz_generated.openapi.go | 22 ++--- ...enerated.openapi_violation_exceptions.list | 1 + pkg/registry/apis/datasource/converter.go | 83 +++++++++---------- 6 files changed, 52 insertions(+), 145 deletions(-) delete mode 100644 pkg/apis/datasource/v0alpha1/config.go diff --git a/pkg/apis/datasource/v0alpha1/config.go b/pkg/apis/datasource/v0alpha1/config.go deleted file mode 100644 index 2e42530df60..00000000000 --- a/pkg/apis/datasource/v0alpha1/config.go +++ /dev/null @@ -1,72 +0,0 @@ -package v0alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" -) - -// +k8s:deepcopy-gen=true -// +k8s:openapi-gen=true -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -type DataSource struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata"` - - // generic config - Spec DataSourceSpec `json:"spec"` - - // Secure values placeholder (true for fields that exist) - Secure common.InlineSecureValues `json:"secure,omitempty"` -} - -// DsAccess represents how the datasource connects to the remote service -// +k8s:openapi-gen=true -// +enum -type DsAccess string - -const ( - // The frontend can connect directly to the remote URL - // This method is discouraged - DsAccessDirect DsAccess = "direct" - - // Connect to the remote datasource through the grafana backend - DsAccessProxy DsAccess = "proxy" -) - -func (dsa DsAccess) String() string { - return string(dsa) -} - -// +k8s:deepcopy-gen=true -// +k8s:openapi-gen=true -type DataSourceSpec struct { - // The diplay name (previously saved as the "name" property) - Title string `json:"title"` - - Access DsAccess `json:"access,omitempty"` - ReadOnly bool `json:"readOnly,omitempty"` - IsDefault bool `json:"isDefault,omitempty"` - - // Server URL - URL string `json:"url,omitempty"` - - User string `json:"user,omitempty"` - Database string `json:"database,omitempty"` - BasicAuth bool `json:"basicAuth,omitempty"` - BasicAuthUser string `json:"basicAuthUser,omitempty"` - WithCredentials bool `json:"withCredentials,omitempty"` - - // Generic unstructured configuration settings - JsonData common.Unstructured `json:"jsonData,omitzero"` -} - -// +k8s:deepcopy-gen=true -// +k8s:openapi-gen=true -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -type DataSourceList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata"` - - Items []DataSource `json:"items"` -} diff --git a/pkg/apis/datasource/v0alpha1/register.go b/pkg/apis/datasource/v0alpha1/register.go index 22c58c232e3..735207452f9 100644 --- a/pkg/apis/datasource/v0alpha1/register.go +++ b/pkg/apis/datasource/v0alpha1/register.go @@ -32,7 +32,7 @@ var DataSourceResourceInfo = utils.NewResourceInfo(GROUP, VERSION, } return []any{ m.Name, - m.Spec.Title, + m.Spec.Object["title"], m.CreationTimestamp.UTC().Format(time.RFC3339), }, nil }, diff --git a/pkg/apis/datasource/v0alpha1/zz_generated.deepcopy.go b/pkg/apis/datasource/v0alpha1/zz_generated.deepcopy.go index c00da0884c0..4488fa37f74 100644 --- a/pkg/apis/datasource/v0alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/datasource/v0alpha1/zz_generated.deepcopy.go @@ -79,23 +79,6 @@ func (in *DataSourceList) DeepCopyObject() runtime.Object { return nil } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DataSourceSpec) DeepCopyInto(out *DataSourceSpec) { - *out = *in - in.JsonData.DeepCopyInto(&out.JsonData) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataSourceSpec. -func (in *DataSourceSpec) DeepCopy() *DataSourceSpec { - if in == nil { - return nil - } - out := new(DataSourceSpec) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HealthCheckResult) DeepCopyInto(out *HealthCheckResult) { *out = *in diff --git a/pkg/apis/datasource/v0alpha1/zz_generated.openapi.go b/pkg/apis/datasource/v0alpha1/zz_generated.openapi.go index 04195be8e3d..7e78e455bae 100644 --- a/pkg/apis/datasource/v0alpha1/zz_generated.openapi.go +++ b/pkg/apis/datasource/v0alpha1/zz_generated.openapi.go @@ -14,10 +14,11 @@ import ( func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { return map[string]common.OpenAPIDefinition{ - "github.com/grafana/grafana/pkg/apis/datasource/v0alpha1.DataSource": schema_pkg_apis_datasource_v0alpha1_DataSource(ref), - "github.com/grafana/grafana/pkg/apis/datasource/v0alpha1.DataSourceList": schema_pkg_apis_datasource_v0alpha1_DataSourceList(ref), - "github.com/grafana/grafana/pkg/apis/datasource/v0alpha1.DataSourceSpec": schema_pkg_apis_datasource_v0alpha1_DataSourceSpec(ref), - "github.com/grafana/grafana/pkg/apis/datasource/v0alpha1.HealthCheckResult": schema_pkg_apis_datasource_v0alpha1_HealthCheckResult(ref), + "github.com/grafana/grafana/pkg/apis/datasource/v0alpha1.DataSource": schema_pkg_apis_datasource_v0alpha1_DataSource(ref), + "github.com/grafana/grafana/pkg/apis/datasource/v0alpha1.DataSourceList": schema_pkg_apis_datasource_v0alpha1_DataSourceList(ref), + "github.com/grafana/grafana/pkg/apis/datasource/v0alpha1.GenericDataSourceSpec": schema_pkg_apis_datasource_v0alpha1_GenericDataSourceSpec(ref), + "github.com/grafana/grafana/pkg/apis/datasource/v0alpha1.HealthCheckResult": schema_pkg_apis_datasource_v0alpha1_HealthCheckResult(ref), + "github.com/grafana/grafana/pkg/apis/datasource/v0alpha1.UnstructuredSpec": UnstructuredSpec{}.OpenAPIDefinition(), } } @@ -49,14 +50,13 @@ func schema_pkg_apis_datasource_v0alpha1_DataSource(ref common.ReferenceCallback }, "spec": { SchemaProps: spec.SchemaProps{ - Description: "generic config", - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/pkg/apis/datasource/v0alpha1.DataSourceSpec"), + Description: "DataSource configuration -- these properties are all visible to anyone able to query the data source from their browser", + Ref: ref("github.com/grafana/grafana/pkg/apis/datasource/v0alpha1.UnstructuredSpec"), }, }, "secure": { SchemaProps: spec.SchemaProps{ - Description: "Secure values placeholder (true for fields that exist)", + Description: "Secure values allows setting values that are never shown to users The returned properties are only the names of the configured values", Type: []string{"object"}, AdditionalProperties: &spec.SchemaOrBool{ Allows: true, @@ -74,7 +74,7 @@ func schema_pkg_apis_datasource_v0alpha1_DataSource(ref common.ReferenceCallback }, }, Dependencies: []string{ - "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.InlineSecureValue", "github.com/grafana/grafana/pkg/apis/datasource/v0alpha1.DataSourceSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.InlineSecureValue", "github.com/grafana/grafana/pkg/apis/datasource/v0alpha1.UnstructuredSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, } } @@ -126,7 +126,7 @@ func schema_pkg_apis_datasource_v0alpha1_DataSourceList(ref common.ReferenceCall } } -func schema_pkg_apis_datasource_v0alpha1_DataSourceSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_pkg_apis_datasource_v0alpha1_GenericDataSourceSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -134,7 +134,7 @@ func schema_pkg_apis_datasource_v0alpha1_DataSourceSpec(ref common.ReferenceCall Properties: map[string]spec.Schema{ "title": { SchemaProps: spec.SchemaProps{ - Description: "The diplay name (previously saved as the \"name\" property)", + Description: "The display name (previously saved as the \"name\" property)", Default: "", Type: []string{"string"}, Format: "", diff --git a/pkg/apis/datasource/v0alpha1/zz_generated.openapi_violation_exceptions.list b/pkg/apis/datasource/v0alpha1/zz_generated.openapi_violation_exceptions.list index 64a405a42a2..918518966c7 100644 --- a/pkg/apis/datasource/v0alpha1/zz_generated.openapi_violation_exceptions.list +++ b/pkg/apis/datasource/v0alpha1/zz_generated.openapi_violation_exceptions.list @@ -1 +1,2 @@ +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/datasource/v0alpha1,UnstructuredSpec,Object API rule violation: streaming_list_type_json_tags,github.com/grafana/grafana/pkg/apis/datasource/v0alpha1,DataSourceList,ListMeta diff --git a/pkg/registry/apis/datasource/converter.go b/pkg/registry/apis/datasource/converter.go index f8ec822a85f..d9ba960ba07 100644 --- a/pkg/registry/apis/datasource/converter.go +++ b/pkg/registry/apis/datasource/converter.go @@ -36,21 +36,22 @@ func (r *converter) asDataSource(ds *datasources.DataSource) (*datasourceV0.Data ResourceVersion: fmt.Sprintf("%d", ds.Updated.UnixMilli()), Generation: int64(ds.Version), }, - Spec: datasourceV0.DataSourceSpec{ - Title: ds.Name, - Access: datasourceV0.DsAccess(ds.Access), - URL: ds.URL, - User: ds.User, - Database: ds.Database, - BasicAuth: ds.BasicAuth, - BasicAuthUser: ds.BasicAuthUser, - WithCredentials: ds.WithCredentials, - IsDefault: ds.IsDefault, - ReadOnly: ds.ReadOnly, - }, + Spec: datasourceV0.UnstructuredSpec{}, Secure: ToInlineSecureValues(ds.Type, ds.UID, maps.Keys(ds.SecureJsonData)), } cfg.UID = gapiutil.CalculateClusterWideUID(cfg) + cfg.Spec.SetTitle(ds.Name). + SetAccess(string(ds.Access)). + SetURL(ds.URL). + SetDatabase(ds.Database). + SetUser(ds.User). + SetDatabase(ds.Database). + SetBasicAuth(ds.BasicAuth). + SetBasicAuthUser(ds.BasicAuthUser). + SetWithCredentials(ds.WithCredentials). + SetIsDefault(ds.IsDefault). + SetReadOnly(ds.ReadOnly). + SetJSONData(ds.JsonData) if ds.ID > 0 { cfg.Labels = map[string]string{ @@ -58,14 +59,6 @@ func (r *converter) asDataSource(ds *datasources.DataSource) (*datasourceV0.Data } } - if ds.JsonData != nil { - val, ok := ds.JsonData.Interface().(map[string]any) - if !ok { - return nil, fmt.Errorf("expected map[string]any jsondata") - } - cfg.Spec.JsonData.Object = val - } - return cfg, nil } @@ -101,24 +94,25 @@ func (r *converter) toAddCommand(ds *datasourceV0.DataSource) (*datasources.AddD } cmd := &datasources.AddDataSourceCommand{ - Name: ds.Spec.Title, + Name: ds.Spec.Title(), UID: ds.Name, OrgID: info.OrgID, Type: r.dstype, - Access: datasources.DsAccess(ds.Spec.Access), - URL: ds.Spec.URL, - Database: ds.Spec.Database, - User: ds.Spec.User, - BasicAuth: ds.Spec.BasicAuth, - BasicAuthUser: ds.Spec.BasicAuthUser, - WithCredentials: ds.Spec.WithCredentials, - IsDefault: ds.Spec.IsDefault, - ReadOnly: ds.Spec.ReadOnly, + Access: datasources.DsAccess(ds.Spec.Access()), + URL: ds.Spec.URL(), + Database: ds.Spec.Database(), + User: ds.Spec.User(), + BasicAuth: ds.Spec.BasicAuth(), + BasicAuthUser: ds.Spec.BasicAuthUser(), + WithCredentials: ds.Spec.WithCredentials(), + IsDefault: ds.Spec.IsDefault(), + ReadOnly: ds.Spec.ReadOnly(), } - if len(ds.Spec.JsonData.Object) > 0 { - cmd.JsonData = simplejson.NewFromAny(ds.Spec.JsonData.Object) + jsonData := ds.Spec.JSONData() + if jsonData != nil { + cmd.JsonData = simplejson.NewFromAny(jsonData) } cmd.SecureJsonData, err = toSecureJsonData(ds) @@ -135,27 +129,28 @@ func (r *converter) toUpdateCommand(ds *datasourceV0.DataSource) (*datasources.U } cmd := &datasources.UpdateDataSourceCommand{ - Name: ds.Spec.Title, + Name: ds.Spec.Title(), UID: ds.Name, OrgID: info.OrgID, Type: r.dstype, - Access: datasources.DsAccess(ds.Spec.Access), - URL: ds.Spec.URL, - Database: ds.Spec.Database, - User: ds.Spec.User, - BasicAuth: ds.Spec.BasicAuth, - BasicAuthUser: ds.Spec.BasicAuthUser, - WithCredentials: ds.Spec.WithCredentials, - IsDefault: ds.Spec.IsDefault, - ReadOnly: ds.Spec.ReadOnly, + Access: datasources.DsAccess(ds.Spec.Access()), + URL: ds.Spec.URL(), + Database: ds.Spec.Database(), + User: ds.Spec.User(), + BasicAuth: ds.Spec.BasicAuth(), + BasicAuthUser: ds.Spec.BasicAuthUser(), + WithCredentials: ds.Spec.WithCredentials(), + IsDefault: ds.Spec.IsDefault(), + ReadOnly: ds.Spec.ReadOnly(), // The only field different than add Version: int(ds.Generation), } - if len(ds.Spec.JsonData.Object) > 0 { - cmd.JsonData = simplejson.NewFromAny(ds.Spec.JsonData.Object) + jsonData := ds.Spec.JSONData() + if jsonData != nil { + cmd.JsonData = simplejson.NewFromAny(jsonData) } cmd.SecureJsonData, err = toSecureJsonData(ds) return cmd, err