unstructured spec

This commit is contained in:
Ryan McKinley
2025-08-20 10:39:14 +03:00
parent b0c9350580
commit bce28b8663
2 changed files with 246 additions and 0 deletions
@@ -0,0 +1,73 @@
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"`
// DataSource configuration -- these properties are all visible
// to anyone able to query the data source from their browser
Spec UnstructuredSpec `json:"spec"`
// Secure values allows setting values that are never shown to users
// The returned properties are only the names of the configured values
Secure common.InlineSecureValues `json:"secure,omitzero,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:openapi-gen=true
type GenericDataSourceSpec struct {
// The display 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"`
}
@@ -0,0 +1,173 @@
package v0alpha1
import (
"encoding/json"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
openapi "k8s.io/kube-openapi/pkg/common"
spec "k8s.io/kube-openapi/pkg/validation/spec"
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
)
// UnstructuredSpec allows any property to be saved into the spec
// Validation will happen from the dynamically loaded schemas for each datasource
// +k8s:deepcopy-gen=true
// +k8s:openapi-gen=true
type UnstructuredSpec common.Unstructured
func (u *UnstructuredSpec) GetString(key string) string {
if u.Object == nil {
return ""
}
v := u.Object[key]
str, _ := v.(string)
return str
}
func (u *UnstructuredSpec) Set(key string, val any) *UnstructuredSpec {
if u.Object == nil {
u.Object = make(map[string]any)
}
if val == nil || val == "" {
delete(u.Object, key)
} else {
u.Object[key] = val
}
return u
}
func (u *UnstructuredSpec) Title() string {
return u.GetString("title")
}
func (u *UnstructuredSpec) SetTitle(v string) *UnstructuredSpec {
return u.Set("title", v)
}
func (u *UnstructuredSpec) URL() string {
return u.GetString("url")
}
func (u *UnstructuredSpec) SetURL(v string) *UnstructuredSpec {
return u.Set("url", v)
}
func (u *UnstructuredSpec) Database() string {
return u.GetString("database")
}
func (u *UnstructuredSpec) SetDatabase(v string) *UnstructuredSpec {
return u.Set("database", v)
}
func (u *UnstructuredSpec) Access() DsAccess {
return DsAccess(u.GetString("access"))
}
func (u *UnstructuredSpec) SetAccess(v string) *UnstructuredSpec {
return u.Set("access", v)
}
func (u *UnstructuredSpec) User() string {
return u.GetString("user")
}
func (u *UnstructuredSpec) SetUser(v string) *UnstructuredSpec {
return u.Set("user", v)
}
func (u *UnstructuredSpec) BasicAuth() bool {
v, _, _ := unstructured.NestedBool(u.Object, "basicAuth")
return v
}
func (u *UnstructuredSpec) SetBasicAuth(v bool) *UnstructuredSpec {
return u.Set("basicAuth", v)
}
func (u *UnstructuredSpec) BasicAuthUser() string {
return u.GetString("basicAuthUser")
}
func (u *UnstructuredSpec) SetBasicAuthUser(v string) *UnstructuredSpec {
return u.Set("basicAuthUser", v)
}
func (u *UnstructuredSpec) WithCredentials() bool {
v, _, _ := unstructured.NestedBool(u.Object, "withCredentials")
return v
}
func (u *UnstructuredSpec) SetWithCredentials(v bool) *UnstructuredSpec {
return u.Set("withCredentials", v)
}
func (u *UnstructuredSpec) IsDefault() bool {
v, _, _ := unstructured.NestedBool(u.Object, "isDefault")
return v
}
func (u *UnstructuredSpec) SetIsDefault(v bool) *UnstructuredSpec {
return u.Set("isDefault", v)
}
func (u *UnstructuredSpec) ReadOnly() bool {
v, _, _ := unstructured.NestedBool(u.Object, "readOnly")
return v
}
func (u *UnstructuredSpec) SetReadOnly(v bool) *UnstructuredSpec {
return u.Set("readOnly", v)
}
func (u *UnstructuredSpec) JSONData() any {
return u.Object["jsonData"]
}
func (u *UnstructuredSpec) SetJSONData(v any) *UnstructuredSpec {
return u.Set("jsonData", v)
}
// The OpenAPI spec uses the generated values from GenericDataSourceSpec, except that it:
// 1. Allows additional properties at the root
// 2. The jsonData field *may* be an raw value OR a map
func (UnstructuredSpec) OpenAPIDefinition() openapi.OpenAPIDefinition {
s := schema_pkg_apis_datasource_v0alpha1_HealthCheckResult(func(path string) spec.Ref {
return spec.MustCreateRef(path)
})
s.Schema.AdditionalProperties = &spec.SchemaOrBool{
Allows: true,
}
return s
}
// MarshalJSON ensures that the unstructured object produces proper
// JSON when passed to Go's standard JSON library.
func (u *UnstructuredSpec) MarshalJSON() ([]byte, error) {
return json.Marshal(u.Object)
}
// UnmarshalJSON ensures that the unstructured object properly decodes
// JSON when passed to Go's standard JSON library.
func (u *UnstructuredSpec) UnmarshalJSON(b []byte) error {
return json.Unmarshal(b, &u.Object)
}
func (u *UnstructuredSpec) DeepCopy() *UnstructuredSpec {
if u == nil {
return nil
}
out := new(UnstructuredSpec)
*out = *u
tmp := common.Unstructured{Object: u.Object}
copy := tmp.DeepCopy()
out.Object = copy.Object
return out
}
func (u *UnstructuredSpec) DeepCopyInto(out *UnstructuredSpec) {
clone := u.DeepCopy()
*out = *clone
}