custom jsonschema for testdata
This commit is contained in:
@@ -7,6 +7,8 @@ import (
|
||||
secret "github.com/grafana/grafana/pkg/apis/secret/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"`
|
||||
@@ -20,6 +22,7 @@ type DataSource struct {
|
||||
}
|
||||
|
||||
// DsAccess represents how the datasource connects to the remote service
|
||||
// +k8s:openapi-gen=true
|
||||
// +enum
|
||||
type DsAccess string
|
||||
|
||||
@@ -36,6 +39,8 @@ 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"`
|
||||
@@ -57,6 +62,8 @@ type DataSourceSpec struct {
|
||||
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"`
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// +k8s:deepcopy-gen=package
|
||||
// +k8s:openapi-gen=true
|
||||
// +k8s:defaulter-gen=TypeMeta
|
||||
// +groupName=datasource.grafana.com
|
||||
|
||||
package v0alpha1
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
"k8s.io/kube-openapi/pkg/spec3"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
|
||||
// Optional extensions for an explict datasource type
|
||||
type DataSourceOpenAPIExtension struct {
|
||||
// When specified, this will replace the default spec
|
||||
DataSourceSpec *spec.Schema `json:"spec,omitempty"`
|
||||
|
||||
// The raw value is never returned in an API response
|
||||
SecureValues []SecureValueInfo `json:"secureValues"`
|
||||
|
||||
// Additional Schemas added to the response
|
||||
Schemas map[string]*spec.Schema `json:"schemas,omitempty"`
|
||||
|
||||
// Resource routes
|
||||
Routes *spec3.Paths `json:"routes,omitempty"`
|
||||
|
||||
// Proxy routes
|
||||
Proxy *spec3.Paths `json:"proxy,omitempty"`
|
||||
}
|
||||
|
||||
type SecureValueInfo struct {
|
||||
// The key
|
||||
Key string `json:"string"`
|
||||
|
||||
// Description
|
||||
Description string `json:"description,omitempty"`
|
||||
|
||||
// Required secure values
|
||||
Required bool `json:"required,omitempty"`
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
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 HealthCheckResult struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by defaulter-gen. DO NOT EDIT.
|
||||
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// RegisterDefaults adds defaulters functions to the given scheme.
|
||||
// Public to allow building arbitrary schemes.
|
||||
// All generated defaulters are covering - they call all nested defaulters.
|
||||
func RegisterDefaults(scheme *runtime.Scheme) error {
|
||||
return nil
|
||||
}
|
||||
@@ -3,19 +3,19 @@ package v0alpha1
|
||||
// Access secure values inside any resource
|
||||
// +k8s:openapi-gen=true
|
||||
type InlineSecureValue struct {
|
||||
// Create a secure value
|
||||
// Create a secure value -- this is only used for POST/PUT
|
||||
// +k8s:validation:minLength=1
|
||||
// +k8s:validation:maxLength=24576
|
||||
Create ExposedSecureValue `json:"create,omitempty"`
|
||||
|
||||
// Reference a shared secret (enterprise only)
|
||||
Reference string `json:"ref,omitempty"`
|
||||
// Name in the secret service (reference)
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
// The resolved UID within the secret service
|
||||
UID string `json:"uid,omitempty"`
|
||||
// The secret is shared (enterprise only)
|
||||
Shared bool `json:"shared,omitempty"`
|
||||
|
||||
// Remove this value -- cascading delete to the secret service if necessary
|
||||
Remove bool `json:"remove,omitzero"`
|
||||
Remove bool `json:"remove,omitempty,omitzero"`
|
||||
}
|
||||
|
||||
// Collection of secure values
|
||||
|
||||
@@ -354,37 +354,35 @@ func schema_pkg_apis_secret_v0alpha1_InlineSecureValue(ref common.ReferenceCallb
|
||||
Properties: map[string]spec.Schema{
|
||||
"create": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Create a secure value",
|
||||
Description: "Create a secure value -- this is only used for POST/PUT",
|
||||
MinLength: ptr.To[int64](1),
|
||||
MaxLength: ptr.To[int64](24576),
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"ref": {
|
||||
"name": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Reference a shared secret (enterprise only)",
|
||||
Description: "Name in the secret service (reference)",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"uid": {
|
||||
"shared": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "The resolved UID within the secret service",
|
||||
Type: []string{"string"},
|
||||
Description: "The secret is shared (enterprise only)",
|
||||
Type: []string{"boolean"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"remove": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Remove this value -- cascading delete to the secret service if necessary",
|
||||
Default: false,
|
||||
Type: []string{"boolean"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"remove"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ API rule violation: names_match,github.com/grafana/grafana/pkg/apis/secret/v0alp
|
||||
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/secret/v0alpha1,AzureCredentials,ClientID
|
||||
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/secret/v0alpha1,AzureCredentials,TenantID
|
||||
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/secret/v0alpha1,GCPCredentials,ProjectID
|
||||
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/secret/v0alpha1,InlineSecureValue,Reference
|
||||
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/secret/v0alpha1,KeeperSpec,Azure
|
||||
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/secret/v0alpha1,KeeperSpec,HashiCorp
|
||||
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/secret/v0alpha1,SecureValueStatus,ExternalID
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package datasource
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -64,8 +66,15 @@ func (r *converter) asDataSource(ds *datasources.DataSource) (*datasourceV0.Data
|
||||
if ds.SecureJsonData != nil {
|
||||
cfg.Secure = make(secretV0.InlineSecureValues)
|
||||
for k := range ds.SecureJsonData {
|
||||
h := sha256.New()
|
||||
h.Write([]byte(ds.Type)) // group+resource
|
||||
h.Write([]byte("|"))
|
||||
h.Write([]byte(ds.UID)) // name
|
||||
h.Write([]byte("|"))
|
||||
h.Write([]byte(k)) // property
|
||||
n := hex.EncodeToString(h.Sum(nil))
|
||||
cfg.Secure[k] = secretV0.InlineSecureValue{
|
||||
UID: "????", // ????
|
||||
Name: "@" + n[0:10], // ??????
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,9 +112,8 @@ func (r *converter) toAddCommand(ds *datasourceV0.DataSource) (*datasources.AddD
|
||||
cmd.JsonData = simplejson.NewFromAny(ds.Spec.JsonData.Object)
|
||||
}
|
||||
|
||||
cmd.SecureJsonData = toSecureJsonData(ds)
|
||||
|
||||
return cmd, nil
|
||||
cmd.SecureJsonData, err = toSecureJsonData(ds)
|
||||
return cmd, err
|
||||
}
|
||||
|
||||
func (r *converter) toUpdateCommand(ds *datasourceV0.DataSource) (*datasources.UpdateDataSourceCommand, error) {
|
||||
@@ -140,17 +148,20 @@ func (r *converter) toUpdateCommand(ds *datasourceV0.DataSource) (*datasources.U
|
||||
if len(ds.Spec.JsonData.Object) > 0 {
|
||||
cmd.JsonData = simplejson.NewFromAny(ds.Spec.JsonData.Object)
|
||||
}
|
||||
cmd.SecureJsonData = toSecureJsonData(ds)
|
||||
return cmd, nil
|
||||
cmd.SecureJsonData, err = toSecureJsonData(ds)
|
||||
return cmd, err
|
||||
}
|
||||
|
||||
func toSecureJsonData(ds *datasourceV0.DataSource) map[string]string {
|
||||
func toSecureJsonData(ds *datasourceV0.DataSource) (map[string]string, error) {
|
||||
if ds == nil || len(ds.Secure) < 1 {
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
secure := map[string]string{}
|
||||
for k, v := range ds.Secure {
|
||||
if v.Shared {
|
||||
return nil, fmt.Errorf("shared secrets not yet supported (%s)", k)
|
||||
}
|
||||
if v.Create != "" {
|
||||
secure[k] = v.Create.DangerouslyExposeAndConsumeValue()
|
||||
}
|
||||
@@ -158,5 +169,5 @@ func toSecureJsonData(ds *datasourceV0.DataSource) map[string]string {
|
||||
secure[k] = "" // Weirdly, this is the best we can do with the legacy API :(
|
||||
}
|
||||
}
|
||||
return secure
|
||||
return secure, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
package datasource
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"maps"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/spec3"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
|
||||
datasourceV0 "github.com/grafana/grafana/pkg/apis/datasource/v0alpha1"
|
||||
secretsV0 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/query/queryschema"
|
||||
)
|
||||
|
||||
func (b *DataSourceAPIBuilder) PostProcessOpenAPI(oas *spec3.OpenAPI) (*spec3.OpenAPI, error) {
|
||||
// The plugin description
|
||||
oas.Info.Description = b.pluginJSON.Info.Description
|
||||
|
||||
// The root api URL
|
||||
root := "/apis/" + b.datasourceResourceInfo.GroupVersion().String() + "/"
|
||||
|
||||
// Add queries to the request properties
|
||||
err := queryschema.AddQueriesToOpenAPI(queryschema.OASQueryOptions{
|
||||
Swagger: oas,
|
||||
PluginJSON: &b.pluginJSON,
|
||||
QueryTypes: b.queryTypes,
|
||||
Root: root,
|
||||
QueryPath: "namespaces/{namespace}/datasources/{name}/query",
|
||||
QueryDescription: fmt.Sprintf("Query the %s datasources", b.pluginJSON.Name),
|
||||
})
|
||||
|
||||
// Set explicit apiVersion and kind on the datasource
|
||||
ds, ok := oas.Components.Schemas["com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("missing DS type")
|
||||
}
|
||||
ds.Properties["apiVersion"] = *spec.StringProperty().WithEnum(b.GetGroupVersion().String())
|
||||
ds.Properties["kind"] = *spec.StringProperty().WithEnum("DataSource")
|
||||
|
||||
custom, err := getCustomOpenAPI(b.pluginJSON.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if custom == nil {
|
||||
return oas, nil // nothing special
|
||||
}
|
||||
|
||||
// Add custom schemas
|
||||
maps.Copy(oas.Components.Schemas, custom.Schemas)
|
||||
|
||||
// Replace the generic DataSourceSpec with the explicit one
|
||||
if custom.DataSourceSpec != nil {
|
||||
oas.Components.Schemas["DataSourceSpec"] = custom.DataSourceSpec
|
||||
ds.Properties["spec"] = spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Ref: spec.MustCreateRef("#/components/schemas/DataSourceSpec"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if custom.SecureValues != nil {
|
||||
example := secretsV0.InlineSecureValues{}
|
||||
ref := spec.MustCreateRef("#/components/schemas/com.github.grafana.grafana.pkg.apis.secret.v0alpha1.InlineSecureValue")
|
||||
secure := &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Properties: make(map[string]spec.Schema),
|
||||
AdditionalProperties: &spec.SchemaOrBool{Allows: false},
|
||||
}}
|
||||
secure.Description = "custom secure value definition"
|
||||
|
||||
for _, v := range custom.SecureValues {
|
||||
secure.Properties[v.Key] = spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: v.Description,
|
||||
Ref: ref,
|
||||
},
|
||||
}
|
||||
if v.Required {
|
||||
secure.Required = append(secure.Required, v.Key)
|
||||
example[v.Key] = secretsV0.InlineSecureValue{Create: "***"}
|
||||
}
|
||||
}
|
||||
|
||||
if len(example) > 0 {
|
||||
secure.Example = example
|
||||
}
|
||||
|
||||
// Link the explicit secure values in the resource
|
||||
oas.Components.Schemas["SecureValues"] = secure
|
||||
ds.Properties["secure"] = spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Ref: spec.MustCreateRef("#/components/schemas/SecureValues"),
|
||||
},
|
||||
}
|
||||
}
|
||||
return oas, err
|
||||
}
|
||||
|
||||
func getCustomOpenAPI(plugin string) (*datasourceV0.DataSourceOpenAPIExtension, error) {
|
||||
if plugin == "grafana-testdata-datasource" {
|
||||
oas := &datasourceV0.DataSourceOpenAPIExtension{
|
||||
SecureValues: []datasourceV0.SecureValueInfo{{
|
||||
Key: "aaa",
|
||||
Description: "describe aaa",
|
||||
Required: true,
|
||||
}, {
|
||||
Key: "bbb",
|
||||
Description: "describe bbb",
|
||||
}},
|
||||
}
|
||||
|
||||
// Dummy spec
|
||||
p := &spec.Schema{} //SchemaProps: spec.SchemaProps{Type: []string{"object"}}}
|
||||
p.Description = "HELLO!"
|
||||
p.Required = []string{"url"}
|
||||
p.AdditionalProperties = &spec.SchemaOrBool{Allows: false}
|
||||
p.Properties = map[string]spec.Schema{
|
||||
"url": *spec.StringProperty(),
|
||||
"str": *spec.StringProperty(), // ??? must this be under jsonData?
|
||||
"int64": *spec.Int64Property(), // ??? must this be under jsonData?
|
||||
}
|
||||
p.Example = map[string]any{
|
||||
"url": "http://xxxx",
|
||||
"int64": 1234,
|
||||
}
|
||||
oas.DataSourceSpec = p
|
||||
|
||||
return oas, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package datasource
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -13,15 +12,12 @@ import (
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||
openapi "k8s.io/kube-openapi/pkg/common"
|
||||
"k8s.io/kube-openapi/pkg/spec3"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
"k8s.io/utils/strings/slices"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
datasourceV0 "github.com/grafana/grafana/pkg/apis/datasource/v0alpha1"
|
||||
queryV0 "github.com/grafana/grafana/pkg/apis/query/v0alpha1"
|
||||
secretsV0 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
|
||||
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
@@ -261,69 +257,3 @@ func (b *DataSourceAPIBuilder) GetOpenAPIDefinitions() openapi.GetOpenAPIDefinit
|
||||
return defs
|
||||
}
|
||||
}
|
||||
|
||||
func (b *DataSourceAPIBuilder) PostProcessOpenAPI(oas *spec3.OpenAPI) (*spec3.OpenAPI, error) {
|
||||
// The plugin description
|
||||
oas.Info.Description = b.pluginJSON.Info.Description
|
||||
|
||||
// The root api URL
|
||||
root := "/apis/" + b.datasourceResourceInfo.GroupVersion().String() + "/"
|
||||
|
||||
// Add queries to the request properties
|
||||
err := queryschema.AddQueriesToOpenAPI(queryschema.OASQueryOptions{
|
||||
Swagger: oas,
|
||||
PluginJSON: &b.pluginJSON,
|
||||
QueryTypes: b.queryTypes,
|
||||
Root: root,
|
||||
QueryPath: "namespaces/{namespace}/datasources/{name}/query",
|
||||
QueryDescription: fmt.Sprintf("Query the %s datasources", b.pluginJSON.Name),
|
||||
})
|
||||
|
||||
ds, ok := oas.Components.Schemas["com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("missing DS type")
|
||||
}
|
||||
|
||||
// Spec
|
||||
p := spec.MapProperty(nil)
|
||||
p.Description = "HELLO!"
|
||||
p.Required = []string{"x", "y"}
|
||||
p.Example = map[string]any{
|
||||
"x": 10,
|
||||
"y": "hello",
|
||||
"url": "http://xxxx",
|
||||
}
|
||||
oas.Components.Schemas["DataSourceSpec"] = p
|
||||
ds.Properties["spec"] = spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Ref: spec.MustCreateRef("#/components/schemas/DataSourceSpec"),
|
||||
},
|
||||
}
|
||||
|
||||
// Secure fields
|
||||
p = spec.MapProperty(&spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Ref: spec.MustCreateRef("#/components/schemas/com.github.grafana.grafana.pkg.apis.secret.v0alpha1.InlineSecureValue"),
|
||||
},
|
||||
})
|
||||
p.Required = []string{"a", "b"}
|
||||
oas.Components.Schemas["SecureFields"] = p
|
||||
ds.Properties["secure"] = spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Ref: spec.MustCreateRef("#/components/schemas/SecureFields"),
|
||||
},
|
||||
}
|
||||
p.Example = secretsV0.InlineSecureValues{
|
||||
"a": secretsV0.InlineSecureValue{
|
||||
Create: "ExampleA",
|
||||
},
|
||||
"b": secretsV0.InlineSecureValue{
|
||||
Create: "ExampleB",
|
||||
},
|
||||
}
|
||||
|
||||
ds.Properties["apiVersion"] = *spec.StringProperty().WithEnum(b.GetGroupVersion().String())
|
||||
ds.Properties["kind"] = *spec.StringProperty().WithEnum("DataSource")
|
||||
|
||||
return oas, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user