Merge branch 'main' into unified-storage-adds-readme-for-quotas

This commit is contained in:
Owen Smallwood
2025-12-09 16:06:57 -06:00
56 changed files with 1114 additions and 933 deletions
+8
View File
@@ -1226,5 +1226,13 @@
"addToProject": {
"url": "https://github.com/orgs/grafana/projects/69"
}
},
{
"type": "label",
"name": "area/suggestions",
"action": "addToProject",
"addToProject": {
"url": "https://github.com/orgs/grafana/projects/56"
}
}
]
+10
View File
@@ -469,5 +469,15 @@
"addToProject": {
"url": "https://github.com/orgs/grafana/projects/190"
}
},
{
"type": "changedfiles",
"matches": [
"public/app/features/panel/suggestions/**/*",
"public/app/plugins/panel/**/suggestions.ts",
"packages/grafana-data/src/types/suggestions*"
],
"action": "updateLabel",
"addLabel": "area/suggestions"
}
]
@@ -85,6 +85,7 @@ area/scenes
area/search
area/security
area/streaming
area/suggestions
area/templating/repeating
area/tooltip
area/transformations
+6 -4
View File
@@ -499,8 +499,8 @@ github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+m
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/edsrzf/mmap-go v1.2.0 h1:hXLYlkbaPzt1SaQk+anYwKSRNhufIDCchSPkUD6dD84=
github.com/edsrzf/mmap-go v1.2.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
@@ -853,6 +853,8 @@ github.com/grafana/grafana-plugin-sdk-go v0.284.0 h1:1bK7eWsnPBLUWDcWJWe218Ik5ad
github.com/grafana/grafana-plugin-sdk-go v0.284.0/go.mod h1:lHPniaSxq3SL5MxDIPy04TYB1jnTp/ivkYO+xn5Rz3E=
github.com/grafana/grafana/apps/example v0.0.0-20251027162426-edef69fdc82b h1:6Bo65etvjQ4tStkaA5+N3A3ENbO4UAWj53TxF6g2Hdk=
github.com/grafana/grafana/apps/example v0.0.0-20251027162426-edef69fdc82b/go.mod h1:6+wASOCN8LWt6FJ8dc0oODUBIEY5XHaE6ABi8g0mR+k=
github.com/grafana/grafana/apps/quotas v0.0.0-20251209183543-1013d74f13f2 h1:rDPMdshj3QMvpXn+wK4T8awF9n2sd8i4YRiGqX2xTvg=
github.com/grafana/grafana/apps/quotas v0.0.0-20251209183543-1013d74f13f2/go.mod h1:M7bV60iRB61y0ISPG1HX/oNLZtlh0ZF22rUYwNkAKjo=
github.com/grafana/grafana/pkg/promlib v0.0.8 h1:VUWsqttdf0wMI4j9OX9oNrykguQpZcruudDAFpJJVw0=
github.com/grafana/grafana/pkg/promlib v0.0.8/go.mod h1:U1ezG/MGaEPoThqsr3lymMPN5yIPdVTJnDZ+wcXT+ao=
github.com/grafana/grafana/pkg/semconv v0.0.0-20250804150913-990f1c69ecc2 h1:A65jWgLk4Re28gIuZcpC0aTh71JZ0ey89hKGE9h543s=
@@ -1416,8 +1418,8 @@ github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah
github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas=
github.com/shadowspore/fossil-delta v0.0.0-20241213113458-1d797d70cbe3 h1:/4/IJi5iyTdh6mqOUaASW148HQpujYiHl0Wl78dSOSc=
github.com/shadowspore/fossil-delta v0.0.0-20241213113458-1d797d70cbe3/go.mod h1:aJIMhRsunltJR926EB2MUg8qHemFQDreSB33pyto2Ps=
github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs=
github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c=
github.com/shirou/gopsutil/v4 v4.25.3 h1:SeA68lsu8gLggyMbmCn8cmp97V1TI9ld9sVzAUcKcKE=
github.com/shirou/gopsutil/v4 v4.25.3/go.mod h1:xbuxyoZj+UsgnZrENu3lQivsngRR5BdjbJwf2fv4szA=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
+1 -1
View File
@@ -16,6 +16,6 @@ v0alpha1Version: {
}
kinds: [
pluginV0Alpha1,
pluginMetaV0Alpha1,
metaV0Alpha1,
]
}
@@ -1,8 +1,7 @@
package plugins
pluginMetaV0Alpha1: {
kind: "PluginMeta"
plural: "pluginsmeta"
metaV0Alpha1: {
kind: "Meta"
scope: "Namespaced"
schema: {
spec: {
@@ -7,33 +7,33 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type PluginMetaClient struct {
client *resource.TypedClient[*PluginMeta, *PluginMetaList]
type MetaClient struct {
client *resource.TypedClient[*Meta, *MetaList]
}
func NewPluginMetaClient(client resource.Client) *PluginMetaClient {
return &PluginMetaClient{
client: resource.NewTypedClient[*PluginMeta, *PluginMetaList](client, PluginMetaKind()),
func NewMetaClient(client resource.Client) *MetaClient {
return &MetaClient{
client: resource.NewTypedClient[*Meta, *MetaList](client, MetaKind()),
}
}
func NewPluginMetaClientFromGenerator(generator resource.ClientGenerator) (*PluginMetaClient, error) {
c, err := generator.ClientFor(PluginMetaKind())
func NewMetaClientFromGenerator(generator resource.ClientGenerator) (*MetaClient, error) {
c, err := generator.ClientFor(MetaKind())
if err != nil {
return nil, err
}
return NewPluginMetaClient(c), nil
return NewMetaClient(c), nil
}
func (c *PluginMetaClient) Get(ctx context.Context, identifier resource.Identifier) (*PluginMeta, error) {
func (c *MetaClient) Get(ctx context.Context, identifier resource.Identifier) (*Meta, error) {
return c.client.Get(ctx, identifier)
}
func (c *PluginMetaClient) List(ctx context.Context, namespace string, opts resource.ListOptions) (*PluginMetaList, error) {
func (c *MetaClient) List(ctx context.Context, namespace string, opts resource.ListOptions) (*MetaList, error) {
return c.client.List(ctx, namespace, opts)
}
func (c *PluginMetaClient) ListAll(ctx context.Context, namespace string, opts resource.ListOptions) (*PluginMetaList, error) {
func (c *MetaClient) ListAll(ctx context.Context, namespace string, opts resource.ListOptions) (*MetaList, error) {
resp, err := c.client.List(ctx, namespace, resource.ListOptions{
ResourceVersion: opts.ResourceVersion,
Limit: opts.Limit,
@@ -61,25 +61,25 @@ func (c *PluginMetaClient) ListAll(ctx context.Context, namespace string, opts r
return resp, nil
}
func (c *PluginMetaClient) Create(ctx context.Context, obj *PluginMeta, opts resource.CreateOptions) (*PluginMeta, error) {
func (c *MetaClient) Create(ctx context.Context, obj *Meta, opts resource.CreateOptions) (*Meta, error) {
// Make sure apiVersion and kind are set
obj.APIVersion = GroupVersion.Identifier()
obj.Kind = PluginMetaKind().Kind()
obj.Kind = MetaKind().Kind()
return c.client.Create(ctx, obj, opts)
}
func (c *PluginMetaClient) Update(ctx context.Context, obj *PluginMeta, opts resource.UpdateOptions) (*PluginMeta, error) {
func (c *MetaClient) Update(ctx context.Context, obj *Meta, opts resource.UpdateOptions) (*Meta, error) {
return c.client.Update(ctx, obj, opts)
}
func (c *PluginMetaClient) Patch(ctx context.Context, identifier resource.Identifier, req resource.PatchRequest, opts resource.PatchOptions) (*PluginMeta, error) {
func (c *MetaClient) Patch(ctx context.Context, identifier resource.Identifier, req resource.PatchRequest, opts resource.PatchOptions) (*Meta, error) {
return c.client.Patch(ctx, identifier, req, opts)
}
func (c *PluginMetaClient) UpdateStatus(ctx context.Context, identifier resource.Identifier, newStatus PluginMetaStatus, opts resource.UpdateOptions) (*PluginMeta, error) {
return c.client.Update(ctx, &PluginMeta{
func (c *MetaClient) UpdateStatus(ctx context.Context, identifier resource.Identifier, newStatus MetaStatus, opts resource.UpdateOptions) (*Meta, error) {
return c.client.Update(ctx, &Meta{
TypeMeta: metav1.TypeMeta{
Kind: PluginMetaKind().Kind(),
Kind: MetaKind().Kind(),
APIVersion: GroupVersion.Identifier(),
},
ObjectMeta: metav1.ObjectMeta{
@@ -94,6 +94,6 @@ func (c *PluginMetaClient) UpdateStatus(ctx context.Context, identifier resource
})
}
func (c *PluginMetaClient) Delete(ctx context.Context, identifier resource.Identifier, opts resource.DeleteOptions) error {
func (c *MetaClient) Delete(ctx context.Context, identifier resource.Identifier, opts resource.DeleteOptions) error {
return c.client.Delete(ctx, identifier, opts)
}
@@ -11,18 +11,18 @@ import (
"github.com/grafana/grafana-app-sdk/resource"
)
// PluginMetaJSONCodec is an implementation of resource.Codec for kubernetes JSON encoding
type PluginMetaJSONCodec struct{}
// MetaJSONCodec is an implementation of resource.Codec for kubernetes JSON encoding
type MetaJSONCodec struct{}
// Read reads JSON-encoded bytes from `reader` and unmarshals them into `into`
func (*PluginMetaJSONCodec) Read(reader io.Reader, into resource.Object) error {
func (*MetaJSONCodec) Read(reader io.Reader, into resource.Object) error {
return json.NewDecoder(reader).Decode(into)
}
// Write writes JSON-encoded bytes into `writer` marshaled from `from`
func (*PluginMetaJSONCodec) Write(writer io.Writer, from resource.Object) error {
func (*MetaJSONCodec) Write(writer io.Writer, from resource.Object) error {
return json.NewEncoder(writer).Encode(from)
}
// Interface compliance checks
var _ resource.Codec = &PluginMetaJSONCodec{}
var _ resource.Codec = &MetaJSONCodec{}
@@ -9,7 +9,7 @@ import (
// metadata contains embedded CommonMetadata and can be extended with custom string fields
// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here
// without external reference as using the CommonMetadata reference breaks thema codegen.
type PluginMetaMetadata struct {
type MetaMetadata struct {
UpdateTimestamp time.Time `json:"updateTimestamp"`
CreatedBy string `json:"createdBy"`
Uid string `json:"uid"`
@@ -22,9 +22,9 @@ type PluginMetaMetadata struct {
Labels map[string]string `json:"labels"`
}
// NewPluginMetaMetadata creates a new PluginMetaMetadata object.
func NewPluginMetaMetadata() *PluginMetaMetadata {
return &PluginMetaMetadata{
// NewMetaMetadata creates a new MetaMetadata object.
func NewMetaMetadata() *MetaMetadata {
return &MetaMetadata{
Finalizers: []string{},
Labels: map[string]string{},
}
@@ -15,22 +15,29 @@ import (
)
// +k8s:openapi-gen=true
type PluginMeta struct {
type Meta struct {
metav1.TypeMeta `json:",inline" yaml:",inline"`
metav1.ObjectMeta `json:"metadata" yaml:"metadata"`
// Spec is the spec of the PluginMeta
Spec PluginMetaSpec `json:"spec" yaml:"spec"`
// Spec is the spec of the Meta
Spec MetaSpec `json:"spec" yaml:"spec"`
Status PluginMetaStatus `json:"status" yaml:"status"`
Status MetaStatus `json:"status" yaml:"status"`
}
func (o *PluginMeta) GetSpec() any {
func NewMeta() *Meta {
return &Meta{
Spec: *NewMetaSpec(),
Status: *NewMetaStatus(),
}
}
func (o *Meta) GetSpec() any {
return o.Spec
}
func (o *PluginMeta) SetSpec(spec any) error {
cast, ok := spec.(PluginMetaSpec)
func (o *Meta) SetSpec(spec any) error {
cast, ok := spec.(MetaSpec)
if !ok {
return fmt.Errorf("cannot set spec type %#v, not of type Spec", spec)
}
@@ -38,13 +45,13 @@ func (o *PluginMeta) SetSpec(spec any) error {
return nil
}
func (o *PluginMeta) GetSubresources() map[string]any {
func (o *Meta) GetSubresources() map[string]any {
return map[string]any{
"status": o.Status,
}
}
func (o *PluginMeta) GetSubresource(name string) (any, bool) {
func (o *Meta) GetSubresource(name string) (any, bool) {
switch name {
case "status":
return o.Status, true
@@ -53,12 +60,12 @@ func (o *PluginMeta) GetSubresource(name string) (any, bool) {
}
}
func (o *PluginMeta) SetSubresource(name string, value any) error {
func (o *Meta) SetSubresource(name string, value any) error {
switch name {
case "status":
cast, ok := value.(PluginMetaStatus)
cast, ok := value.(MetaStatus)
if !ok {
return fmt.Errorf("cannot set status type %#v, not of type PluginMetaStatus", value)
return fmt.Errorf("cannot set status type %#v, not of type MetaStatus", value)
}
o.Status = cast
return nil
@@ -67,7 +74,7 @@ func (o *PluginMeta) SetSubresource(name string, value any) error {
}
}
func (o *PluginMeta) GetStaticMetadata() resource.StaticMetadata {
func (o *Meta) GetStaticMetadata() resource.StaticMetadata {
gvk := o.GroupVersionKind()
return resource.StaticMetadata{
Name: o.ObjectMeta.Name,
@@ -78,7 +85,7 @@ func (o *PluginMeta) GetStaticMetadata() resource.StaticMetadata {
}
}
func (o *PluginMeta) SetStaticMetadata(metadata resource.StaticMetadata) {
func (o *Meta) SetStaticMetadata(metadata resource.StaticMetadata) {
o.Name = metadata.Name
o.Namespace = metadata.Namespace
o.SetGroupVersionKind(schema.GroupVersionKind{
@@ -88,7 +95,7 @@ func (o *PluginMeta) SetStaticMetadata(metadata resource.StaticMetadata) {
})
}
func (o *PluginMeta) GetCommonMetadata() resource.CommonMetadata {
func (o *Meta) GetCommonMetadata() resource.CommonMetadata {
dt := o.DeletionTimestamp
var deletionTimestamp *time.Time
if dt != nil {
@@ -120,7 +127,7 @@ func (o *PluginMeta) GetCommonMetadata() resource.CommonMetadata {
}
}
func (o *PluginMeta) SetCommonMetadata(metadata resource.CommonMetadata) {
func (o *Meta) SetCommonMetadata(metadata resource.CommonMetadata) {
o.UID = types.UID(metadata.UID)
o.ResourceVersion = metadata.ResourceVersion
o.Generation = metadata.Generation
@@ -165,7 +172,7 @@ func (o *PluginMeta) SetCommonMetadata(metadata resource.CommonMetadata) {
}
}
func (o *PluginMeta) GetCreatedBy() string {
func (o *Meta) GetCreatedBy() string {
if o.ObjectMeta.Annotations == nil {
o.ObjectMeta.Annotations = make(map[string]string)
}
@@ -173,7 +180,7 @@ func (o *PluginMeta) GetCreatedBy() string {
return o.ObjectMeta.Annotations["grafana.com/createdBy"]
}
func (o *PluginMeta) SetCreatedBy(createdBy string) {
func (o *Meta) SetCreatedBy(createdBy string) {
if o.ObjectMeta.Annotations == nil {
o.ObjectMeta.Annotations = make(map[string]string)
}
@@ -181,7 +188,7 @@ func (o *PluginMeta) SetCreatedBy(createdBy string) {
o.ObjectMeta.Annotations["grafana.com/createdBy"] = createdBy
}
func (o *PluginMeta) GetUpdateTimestamp() time.Time {
func (o *Meta) GetUpdateTimestamp() time.Time {
if o.ObjectMeta.Annotations == nil {
o.ObjectMeta.Annotations = make(map[string]string)
}
@@ -190,7 +197,7 @@ func (o *PluginMeta) GetUpdateTimestamp() time.Time {
return parsed
}
func (o *PluginMeta) SetUpdateTimestamp(updateTimestamp time.Time) {
func (o *Meta) SetUpdateTimestamp(updateTimestamp time.Time) {
if o.ObjectMeta.Annotations == nil {
o.ObjectMeta.Annotations = make(map[string]string)
}
@@ -198,7 +205,7 @@ func (o *PluginMeta) SetUpdateTimestamp(updateTimestamp time.Time) {
o.ObjectMeta.Annotations["grafana.com/updateTimestamp"] = updateTimestamp.Format(time.RFC3339)
}
func (o *PluginMeta) GetUpdatedBy() string {
func (o *Meta) GetUpdatedBy() string {
if o.ObjectMeta.Annotations == nil {
o.ObjectMeta.Annotations = make(map[string]string)
}
@@ -206,7 +213,7 @@ func (o *PluginMeta) GetUpdatedBy() string {
return o.ObjectMeta.Annotations["grafana.com/updatedBy"]
}
func (o *PluginMeta) SetUpdatedBy(updatedBy string) {
func (o *Meta) SetUpdatedBy(updatedBy string) {
if o.ObjectMeta.Annotations == nil {
o.ObjectMeta.Annotations = make(map[string]string)
}
@@ -214,21 +221,21 @@ func (o *PluginMeta) SetUpdatedBy(updatedBy string) {
o.ObjectMeta.Annotations["grafana.com/updatedBy"] = updatedBy
}
func (o *PluginMeta) Copy() resource.Object {
func (o *Meta) Copy() resource.Object {
return resource.CopyObject(o)
}
func (o *PluginMeta) DeepCopyObject() runtime.Object {
func (o *Meta) DeepCopyObject() runtime.Object {
return o.Copy()
}
func (o *PluginMeta) DeepCopy() *PluginMeta {
cpy := &PluginMeta{}
func (o *Meta) DeepCopy() *Meta {
cpy := &Meta{}
o.DeepCopyInto(cpy)
return cpy
}
func (o *PluginMeta) DeepCopyInto(dst *PluginMeta) {
func (o *Meta) DeepCopyInto(dst *Meta) {
dst.TypeMeta.APIVersion = o.TypeMeta.APIVersion
dst.TypeMeta.Kind = o.TypeMeta.Kind
o.ObjectMeta.DeepCopyInto(&dst.ObjectMeta)
@@ -237,34 +244,34 @@ func (o *PluginMeta) DeepCopyInto(dst *PluginMeta) {
}
// Interface compliance compile-time check
var _ resource.Object = &PluginMeta{}
var _ resource.Object = &Meta{}
// +k8s:openapi-gen=true
type PluginMetaList struct {
type MetaList struct {
metav1.TypeMeta `json:",inline" yaml:",inline"`
metav1.ListMeta `json:"metadata" yaml:"metadata"`
Items []PluginMeta `json:"items" yaml:"items"`
Items []Meta `json:"items" yaml:"items"`
}
func (o *PluginMetaList) DeepCopyObject() runtime.Object {
func (o *MetaList) DeepCopyObject() runtime.Object {
return o.Copy()
}
func (o *PluginMetaList) Copy() resource.ListObject {
cpy := &PluginMetaList{
func (o *MetaList) Copy() resource.ListObject {
cpy := &MetaList{
TypeMeta: o.TypeMeta,
Items: make([]PluginMeta, len(o.Items)),
Items: make([]Meta, len(o.Items)),
}
o.ListMeta.DeepCopyInto(&cpy.ListMeta)
for i := 0; i < len(o.Items); i++ {
if item, ok := o.Items[i].Copy().(*PluginMeta); ok {
if item, ok := o.Items[i].Copy().(*Meta); ok {
cpy.Items[i] = *item
}
}
return cpy
}
func (o *PluginMetaList) GetItems() []resource.Object {
func (o *MetaList) GetItems() []resource.Object {
items := make([]resource.Object, len(o.Items))
for i := 0; i < len(o.Items); i++ {
items[i] = &o.Items[i]
@@ -272,48 +279,48 @@ func (o *PluginMetaList) GetItems() []resource.Object {
return items
}
func (o *PluginMetaList) SetItems(items []resource.Object) {
o.Items = make([]PluginMeta, len(items))
func (o *MetaList) SetItems(items []resource.Object) {
o.Items = make([]Meta, len(items))
for i := 0; i < len(items); i++ {
o.Items[i] = *items[i].(*PluginMeta)
o.Items[i] = *items[i].(*Meta)
}
}
func (o *PluginMetaList) DeepCopy() *PluginMetaList {
cpy := &PluginMetaList{}
func (o *MetaList) DeepCopy() *MetaList {
cpy := &MetaList{}
o.DeepCopyInto(cpy)
return cpy
}
func (o *PluginMetaList) DeepCopyInto(dst *PluginMetaList) {
func (o *MetaList) DeepCopyInto(dst *MetaList) {
resource.CopyObjectInto(dst, o)
}
// Interface compliance compile-time check
var _ resource.ListObject = &PluginMetaList{}
var _ resource.ListObject = &MetaList{}
// Copy methods for all subresource types
// DeepCopy creates a full deep copy of Spec
func (s *PluginMetaSpec) DeepCopy() *PluginMetaSpec {
cpy := &PluginMetaSpec{}
func (s *MetaSpec) DeepCopy() *MetaSpec {
cpy := &MetaSpec{}
s.DeepCopyInto(cpy)
return cpy
}
// DeepCopyInto deep copies Spec into another Spec object
func (s *PluginMetaSpec) DeepCopyInto(dst *PluginMetaSpec) {
func (s *MetaSpec) DeepCopyInto(dst *MetaSpec) {
resource.CopyObjectInto(dst, s)
}
// DeepCopy creates a full deep copy of PluginMetaStatus
func (s *PluginMetaStatus) DeepCopy() *PluginMetaStatus {
cpy := &PluginMetaStatus{}
// DeepCopy creates a full deep copy of MetaStatus
func (s *MetaStatus) DeepCopy() *MetaStatus {
cpy := &MetaStatus{}
s.DeepCopyInto(cpy)
return cpy
}
// DeepCopyInto deep copies PluginMetaStatus into another PluginMetaStatus object
func (s *PluginMetaStatus) DeepCopyInto(dst *PluginMetaStatus) {
// DeepCopyInto deep copies MetaStatus into another MetaStatus object
func (s *MetaStatus) DeepCopyInto(dst *MetaStatus) {
resource.CopyObjectInto(dst, s)
}
@@ -0,0 +1,34 @@
//
// Code generated by grafana-app-sdk. DO NOT EDIT.
//
package v0alpha1
import (
"github.com/grafana/grafana-app-sdk/resource"
)
// schema is unexported to prevent accidental overwrites
var (
schemaMeta = resource.NewSimpleSchema("plugins.grafana.app", "v0alpha1", NewMeta(), &MetaList{}, resource.WithKind("Meta"),
resource.WithPlural("metas"), resource.WithScope(resource.NamespacedScope))
kindMeta = resource.Kind{
Schema: schemaMeta,
Codecs: map[resource.KindEncoding]resource.Codec{
resource.KindEncodingJSON: &MetaJSONCodec{},
},
}
)
// Kind returns a resource.Kind for this Schema with a JSON codec
func MetaKind() resource.Kind {
return kindMeta
}
// Schema returns a resource.SimpleSchema representation of Meta
func MetaSchema() *resource.SimpleSchema {
return schemaMeta
}
// Interface compliance checks
var _ resource.Schema = kindMeta
+474
View File
@@ -0,0 +1,474 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
package v0alpha1
// JSON configuration schema for Grafana plugins
// Converted from: https://github.com/grafana/grafana/blob/main/docs/sources/developers/plugins/plugin.schema.json
// +k8s:openapi-gen=true
type MetaJSONData struct {
// Unique name of the plugin
Id string `json:"id"`
// Plugin type
Type MetaJSONDataType `json:"type"`
// Human-readable name of the plugin
Name string `json:"name"`
// Metadata for the plugin
Info MetaInfo `json:"info"`
// Dependency information
Dependencies MetaDependencies `json:"dependencies"`
// Optional fields
Alerting *bool `json:"alerting,omitempty"`
Annotations *bool `json:"annotations,omitempty"`
AutoEnabled *bool `json:"autoEnabled,omitempty"`
Backend *bool `json:"backend,omitempty"`
BuildMode *string `json:"buildMode,omitempty"`
BuiltIn *bool `json:"builtIn,omitempty"`
Category *MetaJSONDataCategory `json:"category,omitempty"`
EnterpriseFeatures *MetaEnterpriseFeatures `json:"enterpriseFeatures,omitempty"`
Executable *string `json:"executable,omitempty"`
HideFromList *bool `json:"hideFromList,omitempty"`
// +listType=atomic
Includes []MetaInclude `json:"includes,omitempty"`
Logs *bool `json:"logs,omitempty"`
Metrics *bool `json:"metrics,omitempty"`
MultiValueFilterOperators *bool `json:"multiValueFilterOperators,omitempty"`
PascalName *string `json:"pascalName,omitempty"`
Preload *bool `json:"preload,omitempty"`
QueryOptions *MetaQueryOptions `json:"queryOptions,omitempty"`
// +listType=atomic
Routes []MetaRoute `json:"routes,omitempty"`
SkipDataQuery *bool `json:"skipDataQuery,omitempty"`
State *MetaJSONDataState `json:"state,omitempty"`
Streaming *bool `json:"streaming,omitempty"`
Suggestions *bool `json:"suggestions,omitempty"`
Tracing *bool `json:"tracing,omitempty"`
Iam *MetaIAM `json:"iam,omitempty"`
// +listType=atomic
Roles []MetaRole `json:"roles,omitempty"`
Extensions *MetaExtensions `json:"extensions,omitempty"`
}
// NewMetaJSONData creates a new MetaJSONData object.
func NewMetaJSONData() *MetaJSONData {
return &MetaJSONData{
Info: *NewMetaInfo(),
Dependencies: *NewMetaDependencies(),
}
}
// +k8s:openapi-gen=true
type MetaInfo struct {
// Required fields
// +listType=set
Keywords []string `json:"keywords"`
Logos MetaV0alpha1InfoLogos `json:"logos"`
Updated string `json:"updated"`
Version string `json:"version"`
// Optional fields
Author *MetaV0alpha1InfoAuthor `json:"author,omitempty"`
Description *string `json:"description,omitempty"`
// +listType=atomic
Links []MetaV0alpha1InfoLinks `json:"links,omitempty"`
// +listType=atomic
Screenshots []MetaV0alpha1InfoScreenshots `json:"screenshots,omitempty"`
}
// NewMetaInfo creates a new MetaInfo object.
func NewMetaInfo() *MetaInfo {
return &MetaInfo{
Keywords: []string{},
Logos: *NewMetaV0alpha1InfoLogos(),
}
}
// +k8s:openapi-gen=true
type MetaDependencies struct {
// Required field
GrafanaDependency string `json:"grafanaDependency"`
// Optional fields
GrafanaVersion *string `json:"grafanaVersion,omitempty"`
// +listType=set
// +listMapKey=id
Plugins []MetaV0alpha1DependenciesPlugins `json:"plugins,omitempty"`
Extensions *MetaV0alpha1DependenciesExtensions `json:"extensions,omitempty"`
}
// NewMetaDependencies creates a new MetaDependencies object.
func NewMetaDependencies() *MetaDependencies {
return &MetaDependencies{}
}
// +k8s:openapi-gen=true
type MetaEnterpriseFeatures struct {
// Allow additional properties
HealthDiagnosticsErrors *bool `json:"healthDiagnosticsErrors,omitempty"`
}
// NewMetaEnterpriseFeatures creates a new MetaEnterpriseFeatures object.
func NewMetaEnterpriseFeatures() *MetaEnterpriseFeatures {
return &MetaEnterpriseFeatures{
HealthDiagnosticsErrors: (func(input bool) *bool { return &input })(false),
}
}
// +k8s:openapi-gen=true
type MetaInclude struct {
Uid *string `json:"uid,omitempty"`
Type *MetaIncludeType `json:"type,omitempty"`
Name *string `json:"name,omitempty"`
Component *string `json:"component,omitempty"`
Role *MetaIncludeRole `json:"role,omitempty"`
Action *string `json:"action,omitempty"`
Path *string `json:"path,omitempty"`
AddToNav *bool `json:"addToNav,omitempty"`
DefaultNav *bool `json:"defaultNav,omitempty"`
Icon *string `json:"icon,omitempty"`
}
// NewMetaInclude creates a new MetaInclude object.
func NewMetaInclude() *MetaInclude {
return &MetaInclude{}
}
// +k8s:openapi-gen=true
type MetaQueryOptions struct {
MaxDataPoints *bool `json:"maxDataPoints,omitempty"`
MinInterval *bool `json:"minInterval,omitempty"`
CacheTimeout *bool `json:"cacheTimeout,omitempty"`
}
// NewMetaQueryOptions creates a new MetaQueryOptions object.
func NewMetaQueryOptions() *MetaQueryOptions {
return &MetaQueryOptions{}
}
// +k8s:openapi-gen=true
type MetaRoute struct {
Path *string `json:"path,omitempty"`
Method *string `json:"method,omitempty"`
Url *string `json:"url,omitempty"`
ReqSignedIn *bool `json:"reqSignedIn,omitempty"`
ReqRole *string `json:"reqRole,omitempty"`
ReqAction *string `json:"reqAction,omitempty"`
// +listType=atomic
Headers []string `json:"headers,omitempty"`
Body map[string]interface{} `json:"body,omitempty"`
TokenAuth *MetaV0alpha1RouteTokenAuth `json:"tokenAuth,omitempty"`
JwtTokenAuth *MetaV0alpha1RouteJwtTokenAuth `json:"jwtTokenAuth,omitempty"`
// +listType=atomic
UrlParams []MetaV0alpha1RouteUrlParams `json:"urlParams,omitempty"`
}
// NewMetaRoute creates a new MetaRoute object.
func NewMetaRoute() *MetaRoute {
return &MetaRoute{}
}
// +k8s:openapi-gen=true
type MetaIAM struct {
// +listType=atomic
Permissions []MetaV0alpha1IAMPermissions `json:"permissions,omitempty"`
}
// NewMetaIAM creates a new MetaIAM object.
func NewMetaIAM() *MetaIAM {
return &MetaIAM{}
}
// +k8s:openapi-gen=true
type MetaRole struct {
Role *MetaV0alpha1RoleRole `json:"role,omitempty"`
// +listType=set
Grants []string `json:"grants,omitempty"`
}
// NewMetaRole creates a new MetaRole object.
func NewMetaRole() *MetaRole {
return &MetaRole{}
}
// +k8s:openapi-gen=true
type MetaExtensions struct {
// +listType=atomic
AddedComponents []MetaV0alpha1ExtensionsAddedComponents `json:"addedComponents,omitempty"`
// +listType=atomic
AddedLinks []MetaV0alpha1ExtensionsAddedLinks `json:"addedLinks,omitempty"`
// +listType=set
// +listMapKey=id
ExposedComponents []MetaV0alpha1ExtensionsExposedComponents `json:"exposedComponents,omitempty"`
// +listType=set
// +listMapKey=id
ExtensionPoints []MetaV0alpha1ExtensionsExtensionPoints `json:"extensionPoints,omitempty"`
}
// NewMetaExtensions creates a new MetaExtensions object.
func NewMetaExtensions() *MetaExtensions {
return &MetaExtensions{}
}
// +k8s:openapi-gen=true
type MetaSpec struct {
PluginJSON MetaJSONData `json:"pluginJSON"`
}
// NewMetaSpec creates a new MetaSpec object.
func NewMetaSpec() *MetaSpec {
return &MetaSpec{
PluginJSON: *NewMetaJSONData(),
}
}
// +k8s:openapi-gen=true
type MetaV0alpha1InfoLogos struct {
Small string `json:"small"`
Large string `json:"large"`
}
// NewMetaV0alpha1InfoLogos creates a new MetaV0alpha1InfoLogos object.
func NewMetaV0alpha1InfoLogos() *MetaV0alpha1InfoLogos {
return &MetaV0alpha1InfoLogos{}
}
// +k8s:openapi-gen=true
type MetaV0alpha1InfoAuthor struct {
Name *string `json:"name,omitempty"`
Email *string `json:"email,omitempty"`
Url *string `json:"url,omitempty"`
}
// NewMetaV0alpha1InfoAuthor creates a new MetaV0alpha1InfoAuthor object.
func NewMetaV0alpha1InfoAuthor() *MetaV0alpha1InfoAuthor {
return &MetaV0alpha1InfoAuthor{}
}
// +k8s:openapi-gen=true
type MetaV0alpha1InfoLinks struct {
Name *string `json:"name,omitempty"`
Url *string `json:"url,omitempty"`
}
// NewMetaV0alpha1InfoLinks creates a new MetaV0alpha1InfoLinks object.
func NewMetaV0alpha1InfoLinks() *MetaV0alpha1InfoLinks {
return &MetaV0alpha1InfoLinks{}
}
// +k8s:openapi-gen=true
type MetaV0alpha1InfoScreenshots struct {
Name *string `json:"name,omitempty"`
Path *string `json:"path,omitempty"`
}
// NewMetaV0alpha1InfoScreenshots creates a new MetaV0alpha1InfoScreenshots object.
func NewMetaV0alpha1InfoScreenshots() *MetaV0alpha1InfoScreenshots {
return &MetaV0alpha1InfoScreenshots{}
}
// +k8s:openapi-gen=true
type MetaV0alpha1DependenciesPlugins struct {
Id string `json:"id"`
Type MetaV0alpha1DependenciesPluginsType `json:"type"`
Name string `json:"name"`
}
// NewMetaV0alpha1DependenciesPlugins creates a new MetaV0alpha1DependenciesPlugins object.
func NewMetaV0alpha1DependenciesPlugins() *MetaV0alpha1DependenciesPlugins {
return &MetaV0alpha1DependenciesPlugins{}
}
// +k8s:openapi-gen=true
type MetaV0alpha1DependenciesExtensions struct {
// +listType=set
ExposedComponents []string `json:"exposedComponents,omitempty"`
}
// NewMetaV0alpha1DependenciesExtensions creates a new MetaV0alpha1DependenciesExtensions object.
func NewMetaV0alpha1DependenciesExtensions() *MetaV0alpha1DependenciesExtensions {
return &MetaV0alpha1DependenciesExtensions{}
}
// +k8s:openapi-gen=true
type MetaV0alpha1RouteTokenAuth struct {
Url *string `json:"url,omitempty"`
// +listType=set
Scopes []string `json:"scopes,omitempty"`
Params map[string]interface{} `json:"params,omitempty"`
}
// NewMetaV0alpha1RouteTokenAuth creates a new MetaV0alpha1RouteTokenAuth object.
func NewMetaV0alpha1RouteTokenAuth() *MetaV0alpha1RouteTokenAuth {
return &MetaV0alpha1RouteTokenAuth{}
}
// +k8s:openapi-gen=true
type MetaV0alpha1RouteJwtTokenAuth struct {
Url *string `json:"url,omitempty"`
// +listType=set
Scopes []string `json:"scopes,omitempty"`
Params map[string]interface{} `json:"params,omitempty"`
}
// NewMetaV0alpha1RouteJwtTokenAuth creates a new MetaV0alpha1RouteJwtTokenAuth object.
func NewMetaV0alpha1RouteJwtTokenAuth() *MetaV0alpha1RouteJwtTokenAuth {
return &MetaV0alpha1RouteJwtTokenAuth{}
}
// +k8s:openapi-gen=true
type MetaV0alpha1RouteUrlParams struct {
Name *string `json:"name,omitempty"`
Content *string `json:"content,omitempty"`
}
// NewMetaV0alpha1RouteUrlParams creates a new MetaV0alpha1RouteUrlParams object.
func NewMetaV0alpha1RouteUrlParams() *MetaV0alpha1RouteUrlParams {
return &MetaV0alpha1RouteUrlParams{}
}
// +k8s:openapi-gen=true
type MetaV0alpha1IAMPermissions struct {
Action *string `json:"action,omitempty"`
Scope *string `json:"scope,omitempty"`
}
// NewMetaV0alpha1IAMPermissions creates a new MetaV0alpha1IAMPermissions object.
func NewMetaV0alpha1IAMPermissions() *MetaV0alpha1IAMPermissions {
return &MetaV0alpha1IAMPermissions{}
}
// +k8s:openapi-gen=true
type MetaV0alpha1RoleRolePermissions struct {
Action *string `json:"action,omitempty"`
Scope *string `json:"scope,omitempty"`
}
// NewMetaV0alpha1RoleRolePermissions creates a new MetaV0alpha1RoleRolePermissions object.
func NewMetaV0alpha1RoleRolePermissions() *MetaV0alpha1RoleRolePermissions {
return &MetaV0alpha1RoleRolePermissions{}
}
// +k8s:openapi-gen=true
type MetaV0alpha1RoleRole struct {
Name *string `json:"name,omitempty"`
Description *string `json:"description,omitempty"`
// +listType=atomic
Permissions []MetaV0alpha1RoleRolePermissions `json:"permissions,omitempty"`
}
// NewMetaV0alpha1RoleRole creates a new MetaV0alpha1RoleRole object.
func NewMetaV0alpha1RoleRole() *MetaV0alpha1RoleRole {
return &MetaV0alpha1RoleRole{}
}
// +k8s:openapi-gen=true
type MetaV0alpha1ExtensionsAddedComponents struct {
// +listType=set
Targets []string `json:"targets"`
Title string `json:"title"`
Description *string `json:"description,omitempty"`
}
// NewMetaV0alpha1ExtensionsAddedComponents creates a new MetaV0alpha1ExtensionsAddedComponents object.
func NewMetaV0alpha1ExtensionsAddedComponents() *MetaV0alpha1ExtensionsAddedComponents {
return &MetaV0alpha1ExtensionsAddedComponents{
Targets: []string{},
}
}
// +k8s:openapi-gen=true
type MetaV0alpha1ExtensionsAddedLinks struct {
// +listType=set
Targets []string `json:"targets"`
Title string `json:"title"`
Description *string `json:"description,omitempty"`
}
// NewMetaV0alpha1ExtensionsAddedLinks creates a new MetaV0alpha1ExtensionsAddedLinks object.
func NewMetaV0alpha1ExtensionsAddedLinks() *MetaV0alpha1ExtensionsAddedLinks {
return &MetaV0alpha1ExtensionsAddedLinks{
Targets: []string{},
}
}
// +k8s:openapi-gen=true
type MetaV0alpha1ExtensionsExposedComponents struct {
Id string `json:"id"`
Title *string `json:"title,omitempty"`
Description *string `json:"description,omitempty"`
}
// NewMetaV0alpha1ExtensionsExposedComponents creates a new MetaV0alpha1ExtensionsExposedComponents object.
func NewMetaV0alpha1ExtensionsExposedComponents() *MetaV0alpha1ExtensionsExposedComponents {
return &MetaV0alpha1ExtensionsExposedComponents{}
}
// +k8s:openapi-gen=true
type MetaV0alpha1ExtensionsExtensionPoints struct {
Id string `json:"id"`
Title *string `json:"title,omitempty"`
Description *string `json:"description,omitempty"`
}
// NewMetaV0alpha1ExtensionsExtensionPoints creates a new MetaV0alpha1ExtensionsExtensionPoints object.
func NewMetaV0alpha1ExtensionsExtensionPoints() *MetaV0alpha1ExtensionsExtensionPoints {
return &MetaV0alpha1ExtensionsExtensionPoints{}
}
// +k8s:openapi-gen=true
type MetaJSONDataType string
const (
MetaJSONDataTypeApp MetaJSONDataType = "app"
MetaJSONDataTypeDatasource MetaJSONDataType = "datasource"
MetaJSONDataTypePanel MetaJSONDataType = "panel"
MetaJSONDataTypeRenderer MetaJSONDataType = "renderer"
)
// +k8s:openapi-gen=true
type MetaJSONDataCategory string
const (
MetaJSONDataCategoryTsdb MetaJSONDataCategory = "tsdb"
MetaJSONDataCategoryLogging MetaJSONDataCategory = "logging"
MetaJSONDataCategoryCloud MetaJSONDataCategory = "cloud"
MetaJSONDataCategoryTracing MetaJSONDataCategory = "tracing"
MetaJSONDataCategoryProfiling MetaJSONDataCategory = "profiling"
MetaJSONDataCategorySql MetaJSONDataCategory = "sql"
MetaJSONDataCategoryEnterprise MetaJSONDataCategory = "enterprise"
MetaJSONDataCategoryIot MetaJSONDataCategory = "iot"
MetaJSONDataCategoryOther MetaJSONDataCategory = "other"
)
// +k8s:openapi-gen=true
type MetaJSONDataState string
const (
MetaJSONDataStateAlpha MetaJSONDataState = "alpha"
MetaJSONDataStateBeta MetaJSONDataState = "beta"
)
// +k8s:openapi-gen=true
type MetaIncludeType string
const (
MetaIncludeTypeDashboard MetaIncludeType = "dashboard"
MetaIncludeTypePage MetaIncludeType = "page"
MetaIncludeTypePanel MetaIncludeType = "panel"
MetaIncludeTypeDatasource MetaIncludeType = "datasource"
)
// +k8s:openapi-gen=true
type MetaIncludeRole string
const (
MetaIncludeRoleAdmin MetaIncludeRole = "Admin"
MetaIncludeRoleEditor MetaIncludeRole = "Editor"
MetaIncludeRoleViewer MetaIncludeRole = "Viewer"
)
// +k8s:openapi-gen=true
type MetaV0alpha1DependenciesPluginsType string
const (
MetaV0alpha1DependenciesPluginsTypeApp MetaV0alpha1DependenciesPluginsType = "app"
MetaV0alpha1DependenciesPluginsTypeDatasource MetaV0alpha1DependenciesPluginsType = "datasource"
MetaV0alpha1DependenciesPluginsTypePanel MetaV0alpha1DependenciesPluginsType = "panel"
)
@@ -3,42 +3,42 @@
package v0alpha1
// +k8s:openapi-gen=true
type PluginMetastatusOperatorState struct {
type MetastatusOperatorState struct {
// lastEvaluation is the ResourceVersion last evaluated
LastEvaluation string `json:"lastEvaluation"`
// state describes the state of the lastEvaluation.
// It is limited to three possible states for machine evaluation.
State PluginMetaStatusOperatorStateState `json:"state"`
State MetaStatusOperatorStateState `json:"state"`
// descriptiveState is an optional more descriptive state field which has no requirements on format
DescriptiveState *string `json:"descriptiveState,omitempty"`
// details contains any extra information that is operator-specific
Details map[string]interface{} `json:"details,omitempty"`
}
// NewPluginMetastatusOperatorState creates a new PluginMetastatusOperatorState object.
func NewPluginMetastatusOperatorState() *PluginMetastatusOperatorState {
return &PluginMetastatusOperatorState{}
// NewMetastatusOperatorState creates a new MetastatusOperatorState object.
func NewMetastatusOperatorState() *MetastatusOperatorState {
return &MetastatusOperatorState{}
}
// +k8s:openapi-gen=true
type PluginMetaStatus struct {
type MetaStatus struct {
// operatorStates is a map of operator ID to operator state evaluations.
// Any operator which consumes this kind SHOULD add its state evaluation information to this field.
OperatorStates map[string]PluginMetastatusOperatorState `json:"operatorStates,omitempty"`
OperatorStates map[string]MetastatusOperatorState `json:"operatorStates,omitempty"`
// additionalFields is reserved for future use
AdditionalFields map[string]interface{} `json:"additionalFields,omitempty"`
}
// NewPluginMetaStatus creates a new PluginMetaStatus object.
func NewPluginMetaStatus() *PluginMetaStatus {
return &PluginMetaStatus{}
// NewMetaStatus creates a new MetaStatus object.
func NewMetaStatus() *MetaStatus {
return &MetaStatus{}
}
// +k8s:openapi-gen=true
type PluginMetaStatusOperatorStateState string
type MetaStatusOperatorStateState string
const (
PluginMetaStatusOperatorStateStateSuccess PluginMetaStatusOperatorStateState = "success"
PluginMetaStatusOperatorStateStateInProgress PluginMetaStatusOperatorStateState = "in_progress"
PluginMetaStatusOperatorStateStateFailed PluginMetaStatusOperatorStateState = "failed"
MetaStatusOperatorStateStateSuccess MetaStatusOperatorStateState = "success"
MetaStatusOperatorStateStateInProgress MetaStatusOperatorStateState = "in_progress"
MetaStatusOperatorStateStateFailed MetaStatusOperatorStateState = "failed"
)
@@ -25,6 +25,13 @@ type Plugin struct {
Status PluginStatus `json:"status" yaml:"status"`
}
func NewPlugin() *Plugin {
return &Plugin{
Spec: *NewPluginSpec(),
Status: *NewPluginStatus(),
}
}
func (o *Plugin) GetSpec() any {
return o.Spec
}
+1 -1
View File
@@ -10,7 +10,7 @@ import (
// schema is unexported to prevent accidental overwrites
var (
schemaPlugin = resource.NewSimpleSchema("plugins.grafana.app", "v0alpha1", &Plugin{}, &PluginList{}, resource.WithKind("Plugin"),
schemaPlugin = resource.NewSimpleSchema("plugins.grafana.app", "v0alpha1", NewPlugin(), &PluginList{}, resource.WithKind("Plugin"),
resource.WithPlural("plugins"), resource.WithScope(resource.NamespacedScope))
kindPlugin = resource.Kind{
Schema: schemaPlugin,
@@ -1,34 +0,0 @@
//
// Code generated by grafana-app-sdk. DO NOT EDIT.
//
package v0alpha1
import (
"github.com/grafana/grafana-app-sdk/resource"
)
// schema is unexported to prevent accidental overwrites
var (
schemaPluginMeta = resource.NewSimpleSchema("plugins.grafana.app", "v0alpha1", &PluginMeta{}, &PluginMetaList{}, resource.WithKind("PluginMeta"),
resource.WithPlural("pluginmetas"), resource.WithScope(resource.NamespacedScope))
kindPluginMeta = resource.Kind{
Schema: schemaPluginMeta,
Codecs: map[resource.KindEncoding]resource.Codec{
resource.KindEncodingJSON: &PluginMetaJSONCodec{},
},
}
)
// Kind returns a resource.Kind for this Schema with a JSON codec
func PluginMetaKind() resource.Kind {
return kindPluginMeta
}
// Schema returns a resource.SimpleSchema representation of PluginMeta
func PluginMetaSchema() *resource.SimpleSchema {
return schemaPluginMeta
}
// Interface compliance checks
var _ resource.Schema = kindPluginMeta
@@ -1,474 +0,0 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
package v0alpha1
// JSON configuration schema for Grafana plugins
// Converted from: https://github.com/grafana/grafana/blob/main/docs/sources/developers/plugins/plugin.schema.json
// +k8s:openapi-gen=true
type PluginMetaJSONData struct {
// Unique name of the plugin
Id string `json:"id"`
// Plugin type
Type PluginMetaJSONDataType `json:"type"`
// Human-readable name of the plugin
Name string `json:"name"`
// Metadata for the plugin
Info PluginMetaInfo `json:"info"`
// Dependency information
Dependencies PluginMetaDependencies `json:"dependencies"`
// Optional fields
Alerting *bool `json:"alerting,omitempty"`
Annotations *bool `json:"annotations,omitempty"`
AutoEnabled *bool `json:"autoEnabled,omitempty"`
Backend *bool `json:"backend,omitempty"`
BuildMode *string `json:"buildMode,omitempty"`
BuiltIn *bool `json:"builtIn,omitempty"`
Category *PluginMetaJSONDataCategory `json:"category,omitempty"`
EnterpriseFeatures *PluginMetaEnterpriseFeatures `json:"enterpriseFeatures,omitempty"`
Executable *string `json:"executable,omitempty"`
HideFromList *bool `json:"hideFromList,omitempty"`
// +listType=atomic
Includes []PluginMetaInclude `json:"includes,omitempty"`
Logs *bool `json:"logs,omitempty"`
Metrics *bool `json:"metrics,omitempty"`
MultiValueFilterOperators *bool `json:"multiValueFilterOperators,omitempty"`
PascalName *string `json:"pascalName,omitempty"`
Preload *bool `json:"preload,omitempty"`
QueryOptions *PluginMetaQueryOptions `json:"queryOptions,omitempty"`
// +listType=atomic
Routes []PluginMetaRoute `json:"routes,omitempty"`
SkipDataQuery *bool `json:"skipDataQuery,omitempty"`
State *PluginMetaJSONDataState `json:"state,omitempty"`
Streaming *bool `json:"streaming,omitempty"`
Suggestions *bool `json:"suggestions,omitempty"`
Tracing *bool `json:"tracing,omitempty"`
Iam *PluginMetaIAM `json:"iam,omitempty"`
// +listType=atomic
Roles []PluginMetaRole `json:"roles,omitempty"`
Extensions *PluginMetaExtensions `json:"extensions,omitempty"`
}
// NewPluginMetaJSONData creates a new PluginMetaJSONData object.
func NewPluginMetaJSONData() *PluginMetaJSONData {
return &PluginMetaJSONData{
Info: *NewPluginMetaInfo(),
Dependencies: *NewPluginMetaDependencies(),
}
}
// +k8s:openapi-gen=true
type PluginMetaInfo struct {
// Required fields
// +listType=set
Keywords []string `json:"keywords"`
Logos PluginMetaV0alpha1InfoLogos `json:"logos"`
Updated string `json:"updated"`
Version string `json:"version"`
// Optional fields
Author *PluginMetaV0alpha1InfoAuthor `json:"author,omitempty"`
Description *string `json:"description,omitempty"`
// +listType=atomic
Links []PluginMetaV0alpha1InfoLinks `json:"links,omitempty"`
// +listType=atomic
Screenshots []PluginMetaV0alpha1InfoScreenshots `json:"screenshots,omitempty"`
}
// NewPluginMetaInfo creates a new PluginMetaInfo object.
func NewPluginMetaInfo() *PluginMetaInfo {
return &PluginMetaInfo{
Keywords: []string{},
Logos: *NewPluginMetaV0alpha1InfoLogos(),
}
}
// +k8s:openapi-gen=true
type PluginMetaDependencies struct {
// Required field
GrafanaDependency string `json:"grafanaDependency"`
// Optional fields
GrafanaVersion *string `json:"grafanaVersion,omitempty"`
// +listType=set
// +listMapKey=id
Plugins []PluginMetaV0alpha1DependenciesPlugins `json:"plugins,omitempty"`
Extensions *PluginMetaV0alpha1DependenciesExtensions `json:"extensions,omitempty"`
}
// NewPluginMetaDependencies creates a new PluginMetaDependencies object.
func NewPluginMetaDependencies() *PluginMetaDependencies {
return &PluginMetaDependencies{}
}
// +k8s:openapi-gen=true
type PluginMetaEnterpriseFeatures struct {
// Allow additional properties
HealthDiagnosticsErrors *bool `json:"healthDiagnosticsErrors,omitempty"`
}
// NewPluginMetaEnterpriseFeatures creates a new PluginMetaEnterpriseFeatures object.
func NewPluginMetaEnterpriseFeatures() *PluginMetaEnterpriseFeatures {
return &PluginMetaEnterpriseFeatures{
HealthDiagnosticsErrors: (func(input bool) *bool { return &input })(false),
}
}
// +k8s:openapi-gen=true
type PluginMetaInclude struct {
Uid *string `json:"uid,omitempty"`
Type *PluginMetaIncludeType `json:"type,omitempty"`
Name *string `json:"name,omitempty"`
Component *string `json:"component,omitempty"`
Role *PluginMetaIncludeRole `json:"role,omitempty"`
Action *string `json:"action,omitempty"`
Path *string `json:"path,omitempty"`
AddToNav *bool `json:"addToNav,omitempty"`
DefaultNav *bool `json:"defaultNav,omitempty"`
Icon *string `json:"icon,omitempty"`
}
// NewPluginMetaInclude creates a new PluginMetaInclude object.
func NewPluginMetaInclude() *PluginMetaInclude {
return &PluginMetaInclude{}
}
// +k8s:openapi-gen=true
type PluginMetaQueryOptions struct {
MaxDataPoints *bool `json:"maxDataPoints,omitempty"`
MinInterval *bool `json:"minInterval,omitempty"`
CacheTimeout *bool `json:"cacheTimeout,omitempty"`
}
// NewPluginMetaQueryOptions creates a new PluginMetaQueryOptions object.
func NewPluginMetaQueryOptions() *PluginMetaQueryOptions {
return &PluginMetaQueryOptions{}
}
// +k8s:openapi-gen=true
type PluginMetaRoute struct {
Path *string `json:"path,omitempty"`
Method *string `json:"method,omitempty"`
Url *string `json:"url,omitempty"`
ReqSignedIn *bool `json:"reqSignedIn,omitempty"`
ReqRole *string `json:"reqRole,omitempty"`
ReqAction *string `json:"reqAction,omitempty"`
// +listType=atomic
Headers []string `json:"headers,omitempty"`
Body map[string]interface{} `json:"body,omitempty"`
TokenAuth *PluginMetaV0alpha1RouteTokenAuth `json:"tokenAuth,omitempty"`
JwtTokenAuth *PluginMetaV0alpha1RouteJwtTokenAuth `json:"jwtTokenAuth,omitempty"`
// +listType=atomic
UrlParams []PluginMetaV0alpha1RouteUrlParams `json:"urlParams,omitempty"`
}
// NewPluginMetaRoute creates a new PluginMetaRoute object.
func NewPluginMetaRoute() *PluginMetaRoute {
return &PluginMetaRoute{}
}
// +k8s:openapi-gen=true
type PluginMetaIAM struct {
// +listType=atomic
Permissions []PluginMetaV0alpha1IAMPermissions `json:"permissions,omitempty"`
}
// NewPluginMetaIAM creates a new PluginMetaIAM object.
func NewPluginMetaIAM() *PluginMetaIAM {
return &PluginMetaIAM{}
}
// +k8s:openapi-gen=true
type PluginMetaRole struct {
Role *PluginMetaV0alpha1RoleRole `json:"role,omitempty"`
// +listType=set
Grants []string `json:"grants,omitempty"`
}
// NewPluginMetaRole creates a new PluginMetaRole object.
func NewPluginMetaRole() *PluginMetaRole {
return &PluginMetaRole{}
}
// +k8s:openapi-gen=true
type PluginMetaExtensions struct {
// +listType=atomic
AddedComponents []PluginMetaV0alpha1ExtensionsAddedComponents `json:"addedComponents,omitempty"`
// +listType=atomic
AddedLinks []PluginMetaV0alpha1ExtensionsAddedLinks `json:"addedLinks,omitempty"`
// +listType=set
// +listMapKey=id
ExposedComponents []PluginMetaV0alpha1ExtensionsExposedComponents `json:"exposedComponents,omitempty"`
// +listType=set
// +listMapKey=id
ExtensionPoints []PluginMetaV0alpha1ExtensionsExtensionPoints `json:"extensionPoints,omitempty"`
}
// NewPluginMetaExtensions creates a new PluginMetaExtensions object.
func NewPluginMetaExtensions() *PluginMetaExtensions {
return &PluginMetaExtensions{}
}
// +k8s:openapi-gen=true
type PluginMetaSpec struct {
PluginJSON PluginMetaJSONData `json:"pluginJSON"`
}
// NewPluginMetaSpec creates a new PluginMetaSpec object.
func NewPluginMetaSpec() *PluginMetaSpec {
return &PluginMetaSpec{
PluginJSON: *NewPluginMetaJSONData(),
}
}
// +k8s:openapi-gen=true
type PluginMetaV0alpha1InfoLogos struct {
Small string `json:"small"`
Large string `json:"large"`
}
// NewPluginMetaV0alpha1InfoLogos creates a new PluginMetaV0alpha1InfoLogos object.
func NewPluginMetaV0alpha1InfoLogos() *PluginMetaV0alpha1InfoLogos {
return &PluginMetaV0alpha1InfoLogos{}
}
// +k8s:openapi-gen=true
type PluginMetaV0alpha1InfoAuthor struct {
Name *string `json:"name,omitempty"`
Email *string `json:"email,omitempty"`
Url *string `json:"url,omitempty"`
}
// NewPluginMetaV0alpha1InfoAuthor creates a new PluginMetaV0alpha1InfoAuthor object.
func NewPluginMetaV0alpha1InfoAuthor() *PluginMetaV0alpha1InfoAuthor {
return &PluginMetaV0alpha1InfoAuthor{}
}
// +k8s:openapi-gen=true
type PluginMetaV0alpha1InfoLinks struct {
Name *string `json:"name,omitempty"`
Url *string `json:"url,omitempty"`
}
// NewPluginMetaV0alpha1InfoLinks creates a new PluginMetaV0alpha1InfoLinks object.
func NewPluginMetaV0alpha1InfoLinks() *PluginMetaV0alpha1InfoLinks {
return &PluginMetaV0alpha1InfoLinks{}
}
// +k8s:openapi-gen=true
type PluginMetaV0alpha1InfoScreenshots struct {
Name *string `json:"name,omitempty"`
Path *string `json:"path,omitempty"`
}
// NewPluginMetaV0alpha1InfoScreenshots creates a new PluginMetaV0alpha1InfoScreenshots object.
func NewPluginMetaV0alpha1InfoScreenshots() *PluginMetaV0alpha1InfoScreenshots {
return &PluginMetaV0alpha1InfoScreenshots{}
}
// +k8s:openapi-gen=true
type PluginMetaV0alpha1DependenciesPlugins struct {
Id string `json:"id"`
Type PluginMetaV0alpha1DependenciesPluginsType `json:"type"`
Name string `json:"name"`
}
// NewPluginMetaV0alpha1DependenciesPlugins creates a new PluginMetaV0alpha1DependenciesPlugins object.
func NewPluginMetaV0alpha1DependenciesPlugins() *PluginMetaV0alpha1DependenciesPlugins {
return &PluginMetaV0alpha1DependenciesPlugins{}
}
// +k8s:openapi-gen=true
type PluginMetaV0alpha1DependenciesExtensions struct {
// +listType=set
ExposedComponents []string `json:"exposedComponents,omitempty"`
}
// NewPluginMetaV0alpha1DependenciesExtensions creates a new PluginMetaV0alpha1DependenciesExtensions object.
func NewPluginMetaV0alpha1DependenciesExtensions() *PluginMetaV0alpha1DependenciesExtensions {
return &PluginMetaV0alpha1DependenciesExtensions{}
}
// +k8s:openapi-gen=true
type PluginMetaV0alpha1RouteTokenAuth struct {
Url *string `json:"url,omitempty"`
// +listType=set
Scopes []string `json:"scopes,omitempty"`
Params map[string]interface{} `json:"params,omitempty"`
}
// NewPluginMetaV0alpha1RouteTokenAuth creates a new PluginMetaV0alpha1RouteTokenAuth object.
func NewPluginMetaV0alpha1RouteTokenAuth() *PluginMetaV0alpha1RouteTokenAuth {
return &PluginMetaV0alpha1RouteTokenAuth{}
}
// +k8s:openapi-gen=true
type PluginMetaV0alpha1RouteJwtTokenAuth struct {
Url *string `json:"url,omitempty"`
// +listType=set
Scopes []string `json:"scopes,omitempty"`
Params map[string]interface{} `json:"params,omitempty"`
}
// NewPluginMetaV0alpha1RouteJwtTokenAuth creates a new PluginMetaV0alpha1RouteJwtTokenAuth object.
func NewPluginMetaV0alpha1RouteJwtTokenAuth() *PluginMetaV0alpha1RouteJwtTokenAuth {
return &PluginMetaV0alpha1RouteJwtTokenAuth{}
}
// +k8s:openapi-gen=true
type PluginMetaV0alpha1RouteUrlParams struct {
Name *string `json:"name,omitempty"`
Content *string `json:"content,omitempty"`
}
// NewPluginMetaV0alpha1RouteUrlParams creates a new PluginMetaV0alpha1RouteUrlParams object.
func NewPluginMetaV0alpha1RouteUrlParams() *PluginMetaV0alpha1RouteUrlParams {
return &PluginMetaV0alpha1RouteUrlParams{}
}
// +k8s:openapi-gen=true
type PluginMetaV0alpha1IAMPermissions struct {
Action *string `json:"action,omitempty"`
Scope *string `json:"scope,omitempty"`
}
// NewPluginMetaV0alpha1IAMPermissions creates a new PluginMetaV0alpha1IAMPermissions object.
func NewPluginMetaV0alpha1IAMPermissions() *PluginMetaV0alpha1IAMPermissions {
return &PluginMetaV0alpha1IAMPermissions{}
}
// +k8s:openapi-gen=true
type PluginMetaV0alpha1RoleRolePermissions struct {
Action *string `json:"action,omitempty"`
Scope *string `json:"scope,omitempty"`
}
// NewPluginMetaV0alpha1RoleRolePermissions creates a new PluginMetaV0alpha1RoleRolePermissions object.
func NewPluginMetaV0alpha1RoleRolePermissions() *PluginMetaV0alpha1RoleRolePermissions {
return &PluginMetaV0alpha1RoleRolePermissions{}
}
// +k8s:openapi-gen=true
type PluginMetaV0alpha1RoleRole struct {
Name *string `json:"name,omitempty"`
Description *string `json:"description,omitempty"`
// +listType=atomic
Permissions []PluginMetaV0alpha1RoleRolePermissions `json:"permissions,omitempty"`
}
// NewPluginMetaV0alpha1RoleRole creates a new PluginMetaV0alpha1RoleRole object.
func NewPluginMetaV0alpha1RoleRole() *PluginMetaV0alpha1RoleRole {
return &PluginMetaV0alpha1RoleRole{}
}
// +k8s:openapi-gen=true
type PluginMetaV0alpha1ExtensionsAddedComponents struct {
// +listType=set
Targets []string `json:"targets"`
Title string `json:"title"`
Description *string `json:"description,omitempty"`
}
// NewPluginMetaV0alpha1ExtensionsAddedComponents creates a new PluginMetaV0alpha1ExtensionsAddedComponents object.
func NewPluginMetaV0alpha1ExtensionsAddedComponents() *PluginMetaV0alpha1ExtensionsAddedComponents {
return &PluginMetaV0alpha1ExtensionsAddedComponents{
Targets: []string{},
}
}
// +k8s:openapi-gen=true
type PluginMetaV0alpha1ExtensionsAddedLinks struct {
// +listType=set
Targets []string `json:"targets"`
Title string `json:"title"`
Description *string `json:"description,omitempty"`
}
// NewPluginMetaV0alpha1ExtensionsAddedLinks creates a new PluginMetaV0alpha1ExtensionsAddedLinks object.
func NewPluginMetaV0alpha1ExtensionsAddedLinks() *PluginMetaV0alpha1ExtensionsAddedLinks {
return &PluginMetaV0alpha1ExtensionsAddedLinks{
Targets: []string{},
}
}
// +k8s:openapi-gen=true
type PluginMetaV0alpha1ExtensionsExposedComponents struct {
Id string `json:"id"`
Title *string `json:"title,omitempty"`
Description *string `json:"description,omitempty"`
}
// NewPluginMetaV0alpha1ExtensionsExposedComponents creates a new PluginMetaV0alpha1ExtensionsExposedComponents object.
func NewPluginMetaV0alpha1ExtensionsExposedComponents() *PluginMetaV0alpha1ExtensionsExposedComponents {
return &PluginMetaV0alpha1ExtensionsExposedComponents{}
}
// +k8s:openapi-gen=true
type PluginMetaV0alpha1ExtensionsExtensionPoints struct {
Id string `json:"id"`
Title *string `json:"title,omitempty"`
Description *string `json:"description,omitempty"`
}
// NewPluginMetaV0alpha1ExtensionsExtensionPoints creates a new PluginMetaV0alpha1ExtensionsExtensionPoints object.
func NewPluginMetaV0alpha1ExtensionsExtensionPoints() *PluginMetaV0alpha1ExtensionsExtensionPoints {
return &PluginMetaV0alpha1ExtensionsExtensionPoints{}
}
// +k8s:openapi-gen=true
type PluginMetaJSONDataType string
const (
PluginMetaJSONDataTypeApp PluginMetaJSONDataType = "app"
PluginMetaJSONDataTypeDatasource PluginMetaJSONDataType = "datasource"
PluginMetaJSONDataTypePanel PluginMetaJSONDataType = "panel"
PluginMetaJSONDataTypeRenderer PluginMetaJSONDataType = "renderer"
)
// +k8s:openapi-gen=true
type PluginMetaJSONDataCategory string
const (
PluginMetaJSONDataCategoryTsdb PluginMetaJSONDataCategory = "tsdb"
PluginMetaJSONDataCategoryLogging PluginMetaJSONDataCategory = "logging"
PluginMetaJSONDataCategoryCloud PluginMetaJSONDataCategory = "cloud"
PluginMetaJSONDataCategoryTracing PluginMetaJSONDataCategory = "tracing"
PluginMetaJSONDataCategoryProfiling PluginMetaJSONDataCategory = "profiling"
PluginMetaJSONDataCategorySql PluginMetaJSONDataCategory = "sql"
PluginMetaJSONDataCategoryEnterprise PluginMetaJSONDataCategory = "enterprise"
PluginMetaJSONDataCategoryIot PluginMetaJSONDataCategory = "iot"
PluginMetaJSONDataCategoryOther PluginMetaJSONDataCategory = "other"
)
// +k8s:openapi-gen=true
type PluginMetaJSONDataState string
const (
PluginMetaJSONDataStateAlpha PluginMetaJSONDataState = "alpha"
PluginMetaJSONDataStateBeta PluginMetaJSONDataState = "beta"
)
// +k8s:openapi-gen=true
type PluginMetaIncludeType string
const (
PluginMetaIncludeTypeDashboard PluginMetaIncludeType = "dashboard"
PluginMetaIncludeTypePage PluginMetaIncludeType = "page"
PluginMetaIncludeTypePanel PluginMetaIncludeType = "panel"
PluginMetaIncludeTypeDatasource PluginMetaIncludeType = "datasource"
)
// +k8s:openapi-gen=true
type PluginMetaIncludeRole string
const (
PluginMetaIncludeRoleAdmin PluginMetaIncludeRole = "Admin"
PluginMetaIncludeRoleEditor PluginMetaIncludeRole = "Editor"
PluginMetaIncludeRoleViewer PluginMetaIncludeRole = "Viewer"
)
// +k8s:openapi-gen=true
type PluginMetaV0alpha1DependenciesPluginsType string
const (
PluginMetaV0alpha1DependenciesPluginsTypeApp PluginMetaV0alpha1DependenciesPluginsType = "app"
PluginMetaV0alpha1DependenciesPluginsTypeDatasource PluginMetaV0alpha1DependenciesPluginsType = "datasource"
PluginMetaV0alpha1DependenciesPluginsTypePanel PluginMetaV0alpha1DependenciesPluginsType = "panel"
)
File diff suppressed because one or more lines are too long
+3 -3
View File
@@ -43,7 +43,7 @@ func New(cfg app.Config) (app.App, error) {
Kind: pluginsv0alpha1.PluginKind(),
},
{
Kind: pluginsv0alpha1.PluginMetaKind(),
Kind: pluginsv0alpha1.MetaKind(),
},
},
}
@@ -137,9 +137,9 @@ func (p *PluginAppInstaller) InstallAPIs(
return client, nil
}
pluginMetaGVR := pluginsv0alpha1.PluginMetaKind().GroupVersionResource()
pluginMetaGVR := pluginsv0alpha1.MetaKind().GroupVersionResource()
replacedStorage := map[schema.GroupVersionResource]rest.Storage{
pluginMetaGVR: NewPluginMetaStorage(p.metaManager, clientFactory),
pluginMetaGVR: NewMetaStorage(p.metaManager, clientFactory),
}
wrappedServer := &customStorageWrapper{
wrapped: server,
+18 -18
View File
@@ -96,24 +96,24 @@ func (p *CloudProvider) GetMeta(ctx context.Context, pluginID, version string) (
// grafanaComPluginVersionMeta represents the response from grafana.com API
// GET /api/plugins/{pluginId}/versions/{version}
type grafanaComPluginVersionMeta struct {
PluginID string `json:"pluginSlug"`
Version string `json:"version"`
URL string `json:"url"`
Commit string `json:"commit"`
Description string `json:"description"`
Keywords []string `json:"keywords"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
JSON pluginsv0alpha1.PluginMetaJSONData `json:"json"`
Readme string `json:"readme"`
Downloads int `json:"downloads"`
Verified bool `json:"verified"`
Status string `json:"status"`
StatusContext string `json:"statusContext"`
DownloadSlug string `json:"downloadSlug"`
SignatureType string `json:"signatureType"`
SignedByOrg string `json:"signedByOrg"`
SignedByOrgName string `json:"signedByOrgName"`
PluginID string `json:"pluginSlug"`
Version string `json:"version"`
URL string `json:"url"`
Commit string `json:"commit"`
Description string `json:"description"`
Keywords []string `json:"keywords"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
JSON pluginsv0alpha1.MetaJSONData `json:"json"`
Readme string `json:"readme"`
Downloads int `json:"downloads"`
Verified bool `json:"verified"`
Status string `json:"status"`
StatusContext string `json:"statusContext"`
DownloadSlug string `json:"downloadSlug"`
SignatureType string `json:"signatureType"`
SignedByOrg string `json:"signedByOrg"`
SignedByOrgName string `json:"signedByOrgName"`
Packages struct {
Any struct {
Md5 string `json:"md5"`
+4 -4
View File
@@ -19,10 +19,10 @@ func TestCloudProvider_GetMeta(t *testing.T) {
ctx := context.Background()
t.Run("successfully fetches plugin metadata", func(t *testing.T) {
expectedMeta := pluginsv0alpha1.PluginMetaJSONData{
expectedMeta := pluginsv0alpha1.MetaJSONData{
Id: "test-plugin",
Name: "Test Plugin",
Type: pluginsv0alpha1.PluginMetaJSONDataTypeDatasource,
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
}
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -108,10 +108,10 @@ func TestCloudProvider_GetMeta(t *testing.T) {
t.Run("uses custom TTL when provided", func(t *testing.T) {
customTTL := 2 * time.Hour
expectedMeta := pluginsv0alpha1.PluginMetaJSONData{
expectedMeta := pluginsv0alpha1.MetaJSONData{
Id: "test-plugin",
Name: "Test Plugin",
Type: pluginsv0alpha1.PluginMetaJSONDataTypeDatasource,
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
}
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+74 -74
View File
@@ -23,7 +23,7 @@ const (
// CoreProvider retrieves plugin metadata for core plugins.
type CoreProvider struct {
mu sync.RWMutex
loadedPlugins map[string]pluginsv0alpha1.PluginMetaJSONData
loadedPlugins map[string]pluginsv0alpha1.MetaJSONData
initialized bool
ttl time.Duration
}
@@ -36,7 +36,7 @@ func NewCoreProvider() *CoreProvider {
// NewCoreProviderWithTTL creates a new CoreProvider with a custom TTL.
func NewCoreProviderWithTTL(ttl time.Duration) *CoreProvider {
return &CoreProvider{
loadedPlugins: make(map[string]pluginsv0alpha1.PluginMetaJSONData),
loadedPlugins: make(map[string]pluginsv0alpha1.MetaJSONData),
ttl: ttl,
}
}
@@ -119,17 +119,17 @@ func (p *CoreProvider) loadPlugins(ctx context.Context) error {
}
for _, bundle := range ps {
meta := jsonDataToPluginMetaJSONData(bundle.Primary.JSONData)
meta := jsonDataToMetaJSONData(bundle.Primary.JSONData)
p.loadedPlugins[bundle.Primary.JSONData.ID] = meta
}
return nil
}
// jsonDataToPluginMetaJSONData converts a plugins.JSONData to a pluginsv0alpha1.PluginMetaJSONData.
// jsonDataToMetaJSONData converts a plugins.JSONData to a pluginsv0alpha1.MetaJSONData.
// nolint:gocyclo
func jsonDataToPluginMetaJSONData(jsonData plugins.JSONData) pluginsv0alpha1.PluginMetaJSONData {
meta := pluginsv0alpha1.PluginMetaJSONData{
func jsonDataToMetaJSONData(jsonData plugins.JSONData) pluginsv0alpha1.MetaJSONData {
meta := pluginsv0alpha1.MetaJSONData{
Id: jsonData.ID,
Name: jsonData.Name,
}
@@ -137,19 +137,19 @@ func jsonDataToPluginMetaJSONData(jsonData plugins.JSONData) pluginsv0alpha1.Plu
// Map plugin type
switch jsonData.Type {
case plugins.TypeApp:
meta.Type = pluginsv0alpha1.PluginMetaJSONDataTypeApp
meta.Type = pluginsv0alpha1.MetaJSONDataTypeApp
case plugins.TypeDataSource:
meta.Type = pluginsv0alpha1.PluginMetaJSONDataTypeDatasource
meta.Type = pluginsv0alpha1.MetaJSONDataTypeDatasource
case plugins.TypePanel:
meta.Type = pluginsv0alpha1.PluginMetaJSONDataTypePanel
meta.Type = pluginsv0alpha1.MetaJSONDataTypePanel
case plugins.TypeRenderer:
meta.Type = pluginsv0alpha1.PluginMetaJSONDataTypeRenderer
meta.Type = pluginsv0alpha1.MetaJSONDataTypeRenderer
}
// Map Info
meta.Info = pluginsv0alpha1.PluginMetaInfo{
meta.Info = pluginsv0alpha1.MetaInfo{
Keywords: jsonData.Info.Keywords,
Logos: pluginsv0alpha1.PluginMetaV0alpha1InfoLogos{
Logos: pluginsv0alpha1.MetaV0alpha1InfoLogos{
Small: jsonData.Info.Logos.Small,
Large: jsonData.Info.Logos.Large,
},
@@ -162,7 +162,7 @@ func jsonDataToPluginMetaJSONData(jsonData plugins.JSONData) pluginsv0alpha1.Plu
}
if jsonData.Info.Author.Name != "" || jsonData.Info.Author.URL != "" {
author := &pluginsv0alpha1.PluginMetaV0alpha1InfoAuthor{}
author := &pluginsv0alpha1.MetaV0alpha1InfoAuthor{}
if jsonData.Info.Author.Name != "" {
author.Name = &jsonData.Info.Author.Name
}
@@ -173,9 +173,9 @@ func jsonDataToPluginMetaJSONData(jsonData plugins.JSONData) pluginsv0alpha1.Plu
}
if len(jsonData.Info.Links) > 0 {
meta.Info.Links = make([]pluginsv0alpha1.PluginMetaV0alpha1InfoLinks, 0, len(jsonData.Info.Links))
meta.Info.Links = make([]pluginsv0alpha1.MetaV0alpha1InfoLinks, 0, len(jsonData.Info.Links))
for _, link := range jsonData.Info.Links {
v0Link := pluginsv0alpha1.PluginMetaV0alpha1InfoLinks{}
v0Link := pluginsv0alpha1.MetaV0alpha1InfoLinks{}
if link.Name != "" {
v0Link.Name = &link.Name
}
@@ -187,9 +187,9 @@ func jsonDataToPluginMetaJSONData(jsonData plugins.JSONData) pluginsv0alpha1.Plu
}
if len(jsonData.Info.Screenshots) > 0 {
meta.Info.Screenshots = make([]pluginsv0alpha1.PluginMetaV0alpha1InfoScreenshots, 0, len(jsonData.Info.Screenshots))
meta.Info.Screenshots = make([]pluginsv0alpha1.MetaV0alpha1InfoScreenshots, 0, len(jsonData.Info.Screenshots))
for _, screenshot := range jsonData.Info.Screenshots {
v0Screenshot := pluginsv0alpha1.PluginMetaV0alpha1InfoScreenshots{}
v0Screenshot := pluginsv0alpha1.MetaV0alpha1InfoScreenshots{}
if screenshot.Name != "" {
v0Screenshot.Name = &screenshot.Name
}
@@ -201,7 +201,7 @@ func jsonDataToPluginMetaJSONData(jsonData plugins.JSONData) pluginsv0alpha1.Plu
}
// Map Dependencies
meta.Dependencies = pluginsv0alpha1.PluginMetaDependencies{
meta.Dependencies = pluginsv0alpha1.MetaDependencies{
GrafanaDependency: jsonData.Dependencies.GrafanaDependency,
}
@@ -210,18 +210,18 @@ func jsonDataToPluginMetaJSONData(jsonData plugins.JSONData) pluginsv0alpha1.Plu
}
if len(jsonData.Dependencies.Plugins) > 0 {
meta.Dependencies.Plugins = make([]pluginsv0alpha1.PluginMetaV0alpha1DependenciesPlugins, 0, len(jsonData.Dependencies.Plugins))
meta.Dependencies.Plugins = make([]pluginsv0alpha1.MetaV0alpha1DependenciesPlugins, 0, len(jsonData.Dependencies.Plugins))
for _, dep := range jsonData.Dependencies.Plugins {
var depType pluginsv0alpha1.PluginMetaV0alpha1DependenciesPluginsType
var depType pluginsv0alpha1.MetaV0alpha1DependenciesPluginsType
switch dep.Type {
case "app":
depType = pluginsv0alpha1.PluginMetaV0alpha1DependenciesPluginsTypeApp
depType = pluginsv0alpha1.MetaV0alpha1DependenciesPluginsTypeApp
case "datasource":
depType = pluginsv0alpha1.PluginMetaV0alpha1DependenciesPluginsTypeDatasource
depType = pluginsv0alpha1.MetaV0alpha1DependenciesPluginsTypeDatasource
case "panel":
depType = pluginsv0alpha1.PluginMetaV0alpha1DependenciesPluginsTypePanel
depType = pluginsv0alpha1.MetaV0alpha1DependenciesPluginsTypePanel
}
meta.Dependencies.Plugins = append(meta.Dependencies.Plugins, pluginsv0alpha1.PluginMetaV0alpha1DependenciesPlugins{
meta.Dependencies.Plugins = append(meta.Dependencies.Plugins, pluginsv0alpha1.MetaV0alpha1DependenciesPlugins{
Id: dep.ID,
Type: depType,
Name: dep.Name,
@@ -230,7 +230,7 @@ func jsonDataToPluginMetaJSONData(jsonData plugins.JSONData) pluginsv0alpha1.Plu
}
if len(jsonData.Dependencies.Extensions.ExposedComponents) > 0 {
meta.Dependencies.Extensions = &pluginsv0alpha1.PluginMetaV0alpha1DependenciesExtensions{
meta.Dependencies.Extensions = &pluginsv0alpha1.MetaV0alpha1DependenciesExtensions{
ExposedComponents: jsonData.Dependencies.Extensions.ExposedComponents,
}
}
@@ -278,40 +278,40 @@ func jsonDataToPluginMetaJSONData(jsonData plugins.JSONData) pluginsv0alpha1.Plu
// Map category
if jsonData.Category != "" {
var category pluginsv0alpha1.PluginMetaJSONDataCategory
var category pluginsv0alpha1.MetaJSONDataCategory
switch jsonData.Category {
case "tsdb":
category = pluginsv0alpha1.PluginMetaJSONDataCategoryTsdb
category = pluginsv0alpha1.MetaJSONDataCategoryTsdb
case "logging":
category = pluginsv0alpha1.PluginMetaJSONDataCategoryLogging
category = pluginsv0alpha1.MetaJSONDataCategoryLogging
case "cloud":
category = pluginsv0alpha1.PluginMetaJSONDataCategoryCloud
category = pluginsv0alpha1.MetaJSONDataCategoryCloud
case "tracing":
category = pluginsv0alpha1.PluginMetaJSONDataCategoryTracing
category = pluginsv0alpha1.MetaJSONDataCategoryTracing
case "profiling":
category = pluginsv0alpha1.PluginMetaJSONDataCategoryProfiling
category = pluginsv0alpha1.MetaJSONDataCategoryProfiling
case "sql":
category = pluginsv0alpha1.PluginMetaJSONDataCategorySql
category = pluginsv0alpha1.MetaJSONDataCategorySql
case "enterprise":
category = pluginsv0alpha1.PluginMetaJSONDataCategoryEnterprise
category = pluginsv0alpha1.MetaJSONDataCategoryEnterprise
case "iot":
category = pluginsv0alpha1.PluginMetaJSONDataCategoryIot
category = pluginsv0alpha1.MetaJSONDataCategoryIot
case "other":
category = pluginsv0alpha1.PluginMetaJSONDataCategoryOther
category = pluginsv0alpha1.MetaJSONDataCategoryOther
default:
category = pluginsv0alpha1.PluginMetaJSONDataCategoryOther
category = pluginsv0alpha1.MetaJSONDataCategoryOther
}
meta.Category = &category
}
// Map state
if jsonData.State != "" {
var state pluginsv0alpha1.PluginMetaJSONDataState
var state pluginsv0alpha1.MetaJSONDataState
switch jsonData.State {
case plugins.ReleaseStateAlpha:
state = pluginsv0alpha1.PluginMetaJSONDataStateAlpha
state = pluginsv0alpha1.MetaJSONDataStateAlpha
case plugins.ReleaseStateBeta:
state = pluginsv0alpha1.PluginMetaJSONDataStateBeta
state = pluginsv0alpha1.MetaJSONDataStateBeta
default:
}
if state != "" {
@@ -326,7 +326,7 @@ func jsonDataToPluginMetaJSONData(jsonData plugins.JSONData) pluginsv0alpha1.Plu
// Map QueryOptions
if len(jsonData.QueryOptions) > 0 {
queryOptions := &pluginsv0alpha1.PluginMetaQueryOptions{}
queryOptions := &pluginsv0alpha1.MetaQueryOptions{}
if val, ok := jsonData.QueryOptions["maxDataPoints"]; ok {
queryOptions.MaxDataPoints = &val
}
@@ -341,23 +341,23 @@ func jsonDataToPluginMetaJSONData(jsonData plugins.JSONData) pluginsv0alpha1.Plu
// Map Includes
if len(jsonData.Includes) > 0 {
meta.Includes = make([]pluginsv0alpha1.PluginMetaInclude, 0, len(jsonData.Includes))
meta.Includes = make([]pluginsv0alpha1.MetaInclude, 0, len(jsonData.Includes))
for _, include := range jsonData.Includes {
v0Include := pluginsv0alpha1.PluginMetaInclude{}
v0Include := pluginsv0alpha1.MetaInclude{}
if include.UID != "" {
v0Include.Uid = &include.UID
}
if include.Type != "" {
var includeType pluginsv0alpha1.PluginMetaIncludeType
var includeType pluginsv0alpha1.MetaIncludeType
switch include.Type {
case "dashboard":
includeType = pluginsv0alpha1.PluginMetaIncludeTypeDashboard
includeType = pluginsv0alpha1.MetaIncludeTypeDashboard
case "page":
includeType = pluginsv0alpha1.PluginMetaIncludeTypePage
includeType = pluginsv0alpha1.MetaIncludeTypePage
case "panel":
includeType = pluginsv0alpha1.PluginMetaIncludeTypePanel
includeType = pluginsv0alpha1.MetaIncludeTypePanel
case "datasource":
includeType = pluginsv0alpha1.PluginMetaIncludeTypeDatasource
includeType = pluginsv0alpha1.MetaIncludeTypeDatasource
}
v0Include.Type = &includeType
}
@@ -368,14 +368,14 @@ func jsonDataToPluginMetaJSONData(jsonData plugins.JSONData) pluginsv0alpha1.Plu
v0Include.Component = &include.Component
}
if include.Role != "" {
var role pluginsv0alpha1.PluginMetaIncludeRole
var role pluginsv0alpha1.MetaIncludeRole
switch include.Role {
case "Admin":
role = pluginsv0alpha1.PluginMetaIncludeRoleAdmin
role = pluginsv0alpha1.MetaIncludeRoleAdmin
case "Editor":
role = pluginsv0alpha1.PluginMetaIncludeRoleEditor
role = pluginsv0alpha1.MetaIncludeRoleEditor
case "Viewer":
role = pluginsv0alpha1.PluginMetaIncludeRoleViewer
role = pluginsv0alpha1.MetaIncludeRoleViewer
}
v0Include.Role = &role
}
@@ -400,9 +400,9 @@ func jsonDataToPluginMetaJSONData(jsonData plugins.JSONData) pluginsv0alpha1.Plu
// Map Routes
if len(jsonData.Routes) > 0 {
meta.Routes = make([]pluginsv0alpha1.PluginMetaRoute, 0, len(jsonData.Routes))
meta.Routes = make([]pluginsv0alpha1.MetaRoute, 0, len(jsonData.Routes))
for _, route := range jsonData.Routes {
v0Route := pluginsv0alpha1.PluginMetaRoute{}
v0Route := pluginsv0alpha1.MetaRoute{}
if route.Path != "" {
v0Route.Path = &route.Path
}
@@ -427,9 +427,9 @@ func jsonDataToPluginMetaJSONData(jsonData plugins.JSONData) pluginsv0alpha1.Plu
v0Route.Headers = headers
}
if len(route.URLParams) > 0 {
v0Route.UrlParams = make([]pluginsv0alpha1.PluginMetaV0alpha1RouteUrlParams, 0, len(route.URLParams))
v0Route.UrlParams = make([]pluginsv0alpha1.MetaV0alpha1RouteUrlParams, 0, len(route.URLParams))
for _, param := range route.URLParams {
v0Param := pluginsv0alpha1.PluginMetaV0alpha1RouteUrlParams{}
v0Param := pluginsv0alpha1.MetaV0alpha1RouteUrlParams{}
if param.Name != "" {
v0Param.Name = &param.Name
}
@@ -440,7 +440,7 @@ func jsonDataToPluginMetaJSONData(jsonData plugins.JSONData) pluginsv0alpha1.Plu
}
}
if route.TokenAuth != nil {
v0Route.TokenAuth = &pluginsv0alpha1.PluginMetaV0alpha1RouteTokenAuth{}
v0Route.TokenAuth = &pluginsv0alpha1.MetaV0alpha1RouteTokenAuth{}
if route.TokenAuth.Url != "" {
v0Route.TokenAuth.Url = &route.TokenAuth.Url
}
@@ -455,7 +455,7 @@ func jsonDataToPluginMetaJSONData(jsonData plugins.JSONData) pluginsv0alpha1.Plu
}
}
if route.JwtTokenAuth != nil {
v0Route.JwtTokenAuth = &pluginsv0alpha1.PluginMetaV0alpha1RouteJwtTokenAuth{}
v0Route.JwtTokenAuth = &pluginsv0alpha1.MetaV0alpha1RouteJwtTokenAuth{}
if route.JwtTokenAuth.Url != "" {
v0Route.JwtTokenAuth.Url = &route.JwtTokenAuth.Url
}
@@ -482,12 +482,12 @@ func jsonDataToPluginMetaJSONData(jsonData plugins.JSONData) pluginsv0alpha1.Plu
// Map Extensions
if len(jsonData.Extensions.AddedLinks) > 0 || len(jsonData.Extensions.AddedComponents) > 0 ||
len(jsonData.Extensions.ExposedComponents) > 0 || len(jsonData.Extensions.ExtensionPoints) > 0 {
extensions := &pluginsv0alpha1.PluginMetaExtensions{}
extensions := &pluginsv0alpha1.MetaExtensions{}
if len(jsonData.Extensions.AddedLinks) > 0 {
extensions.AddedLinks = make([]pluginsv0alpha1.PluginMetaV0alpha1ExtensionsAddedLinks, 0, len(jsonData.Extensions.AddedLinks))
extensions.AddedLinks = make([]pluginsv0alpha1.MetaV0alpha1ExtensionsAddedLinks, 0, len(jsonData.Extensions.AddedLinks))
for _, link := range jsonData.Extensions.AddedLinks {
v0Link := pluginsv0alpha1.PluginMetaV0alpha1ExtensionsAddedLinks{
v0Link := pluginsv0alpha1.MetaV0alpha1ExtensionsAddedLinks{
Targets: link.Targets,
Title: link.Title,
}
@@ -499,9 +499,9 @@ func jsonDataToPluginMetaJSONData(jsonData plugins.JSONData) pluginsv0alpha1.Plu
}
if len(jsonData.Extensions.AddedComponents) > 0 {
extensions.AddedComponents = make([]pluginsv0alpha1.PluginMetaV0alpha1ExtensionsAddedComponents, 0, len(jsonData.Extensions.AddedComponents))
extensions.AddedComponents = make([]pluginsv0alpha1.MetaV0alpha1ExtensionsAddedComponents, 0, len(jsonData.Extensions.AddedComponents))
for _, comp := range jsonData.Extensions.AddedComponents {
v0Comp := pluginsv0alpha1.PluginMetaV0alpha1ExtensionsAddedComponents{
v0Comp := pluginsv0alpha1.MetaV0alpha1ExtensionsAddedComponents{
Targets: comp.Targets,
Title: comp.Title,
}
@@ -513,9 +513,9 @@ func jsonDataToPluginMetaJSONData(jsonData plugins.JSONData) pluginsv0alpha1.Plu
}
if len(jsonData.Extensions.ExposedComponents) > 0 {
extensions.ExposedComponents = make([]pluginsv0alpha1.PluginMetaV0alpha1ExtensionsExposedComponents, 0, len(jsonData.Extensions.ExposedComponents))
extensions.ExposedComponents = make([]pluginsv0alpha1.MetaV0alpha1ExtensionsExposedComponents, 0, len(jsonData.Extensions.ExposedComponents))
for _, comp := range jsonData.Extensions.ExposedComponents {
v0Comp := pluginsv0alpha1.PluginMetaV0alpha1ExtensionsExposedComponents{
v0Comp := pluginsv0alpha1.MetaV0alpha1ExtensionsExposedComponents{
Id: comp.Id,
}
if comp.Title != "" {
@@ -529,9 +529,9 @@ func jsonDataToPluginMetaJSONData(jsonData plugins.JSONData) pluginsv0alpha1.Plu
}
if len(jsonData.Extensions.ExtensionPoints) > 0 {
extensions.ExtensionPoints = make([]pluginsv0alpha1.PluginMetaV0alpha1ExtensionsExtensionPoints, 0, len(jsonData.Extensions.ExtensionPoints))
extensions.ExtensionPoints = make([]pluginsv0alpha1.MetaV0alpha1ExtensionsExtensionPoints, 0, len(jsonData.Extensions.ExtensionPoints))
for _, point := range jsonData.Extensions.ExtensionPoints {
v0Point := pluginsv0alpha1.PluginMetaV0alpha1ExtensionsExtensionPoints{
v0Point := pluginsv0alpha1.MetaV0alpha1ExtensionsExtensionPoints{
Id: point.Id,
}
if point.Title != "" {
@@ -549,13 +549,13 @@ func jsonDataToPluginMetaJSONData(jsonData plugins.JSONData) pluginsv0alpha1.Plu
// Map Roles
if len(jsonData.Roles) > 0 {
meta.Roles = make([]pluginsv0alpha1.PluginMetaRole, 0, len(jsonData.Roles))
meta.Roles = make([]pluginsv0alpha1.MetaRole, 0, len(jsonData.Roles))
for _, role := range jsonData.Roles {
v0Role := pluginsv0alpha1.PluginMetaRole{
v0Role := pluginsv0alpha1.MetaRole{
Grants: role.Grants,
}
if role.Role.Name != "" || role.Role.Description != "" || len(role.Role.Permissions) > 0 {
v0RoleRole := &pluginsv0alpha1.PluginMetaV0alpha1RoleRole{}
v0RoleRole := &pluginsv0alpha1.MetaV0alpha1RoleRole{}
if role.Role.Name != "" {
v0RoleRole.Name = &role.Role.Name
}
@@ -563,9 +563,9 @@ func jsonDataToPluginMetaJSONData(jsonData plugins.JSONData) pluginsv0alpha1.Plu
v0RoleRole.Description = &role.Role.Description
}
if len(role.Role.Permissions) > 0 {
v0RoleRole.Permissions = make([]pluginsv0alpha1.PluginMetaV0alpha1RoleRolePermissions, 0, len(role.Role.Permissions))
v0RoleRole.Permissions = make([]pluginsv0alpha1.MetaV0alpha1RoleRolePermissions, 0, len(role.Role.Permissions))
for _, perm := range role.Role.Permissions {
v0Perm := pluginsv0alpha1.PluginMetaV0alpha1RoleRolePermissions{}
v0Perm := pluginsv0alpha1.MetaV0alpha1RoleRolePermissions{}
if perm.Action != "" {
v0Perm.Action = &perm.Action
}
@@ -583,11 +583,11 @@ func jsonDataToPluginMetaJSONData(jsonData plugins.JSONData) pluginsv0alpha1.Plu
// Map IAM
if jsonData.IAM != nil && len(jsonData.IAM.Permissions) > 0 {
iam := &pluginsv0alpha1.PluginMetaIAM{
Permissions: make([]pluginsv0alpha1.PluginMetaV0alpha1IAMPermissions, 0, len(jsonData.IAM.Permissions)),
iam := &pluginsv0alpha1.MetaIAM{
Permissions: make([]pluginsv0alpha1.MetaV0alpha1IAMPermissions, 0, len(jsonData.IAM.Permissions)),
}
for _, perm := range jsonData.IAM.Permissions {
v0Perm := pluginsv0alpha1.PluginMetaV0alpha1IAMPermissions{}
v0Perm := pluginsv0alpha1.MetaV0alpha1IAMPermissions{}
if perm.Action != "" {
v0Perm.Action = &perm.Action
}
+9 -9
View File
@@ -22,10 +22,10 @@ func TestCoreProvider_GetMeta(t *testing.T) {
t.Run("returns cached plugin when available", func(t *testing.T) {
provider := NewCoreProvider()
expectedMeta := pluginsv0alpha1.PluginMetaJSONData{
expectedMeta := pluginsv0alpha1.MetaJSONData{
Id: "test-plugin",
Name: "Test Plugin",
Type: pluginsv0alpha1.PluginMetaJSONDataTypeDatasource,
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
}
provider.mu.Lock()
@@ -58,10 +58,10 @@ func TestCoreProvider_GetMeta(t *testing.T) {
t.Run("ignores version parameter", func(t *testing.T) {
provider := NewCoreProvider()
expectedMeta := pluginsv0alpha1.PluginMetaJSONData{
expectedMeta := pluginsv0alpha1.MetaJSONData{
Id: "test-plugin",
Name: "Test Plugin",
Type: pluginsv0alpha1.PluginMetaJSONDataTypeDatasource,
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
}
provider.mu.Lock()
@@ -81,10 +81,10 @@ func TestCoreProvider_GetMeta(t *testing.T) {
customTTL := 2 * time.Hour
provider := NewCoreProviderWithTTL(customTTL)
expectedMeta := pluginsv0alpha1.PluginMetaJSONData{
expectedMeta := pluginsv0alpha1.MetaJSONData{
Id: "test-plugin",
Name: "Test Plugin",
Type: pluginsv0alpha1.PluginMetaJSONDataTypeDatasource,
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
}
provider.mu.Lock()
@@ -271,11 +271,11 @@ func TestJsonDataToMeta(t *testing.T) {
},
}
meta := jsonDataToPluginMetaJSONData(jsonData)
meta := jsonDataToMetaJSONData(jsonData)
assert.Equal(t, "test-plugin", meta.Id)
assert.Equal(t, "Test Plugin", meta.Name)
assert.Equal(t, pluginsv0alpha1.PluginMetaJSONDataTypeDatasource, meta.Type)
assert.Equal(t, pluginsv0alpha1.MetaJSONDataTypeDatasource, meta.Type)
assert.Equal(t, "1.0.0", meta.Info.Version)
assert.Equal(t, "Test description", *meta.Info.Description)
assert.Equal(t, []string{"test", "plugin"}, meta.Info.Keywords)
@@ -293,7 +293,7 @@ func TestJsonDataToMeta(t *testing.T) {
},
}
meta := jsonDataToPluginMetaJSONData(jsonData)
meta := jsonDataToMetaJSONData(jsonData)
assert.Nil(t, meta.Info.Description)
assert.Nil(t, meta.Info.Author)
+1 -1
View File
@@ -16,7 +16,7 @@ const (
// cachedMeta represents a cached metadata entry with expiration time
type cachedMeta struct {
meta pluginsv0alpha1.PluginMetaJSONData
meta pluginsv0alpha1.MetaJSONData
ttl time.Duration
expiresAt time.Time
}
+22 -22
View File
@@ -35,10 +35,10 @@ func TestProviderManager_GetMeta(t *testing.T) {
ctx := context.Background()
t.Run("returns cached result when available and not expired", func(t *testing.T) {
cachedMeta := pluginsv0alpha1.PluginMetaJSONData{
cachedMeta := pluginsv0alpha1.MetaJSONData{
Id: "test-plugin",
Name: "Test Plugin",
Type: pluginsv0alpha1.PluginMetaJSONDataTypeDatasource,
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
}
provider := &mockProvider{
@@ -60,7 +60,7 @@ func TestProviderManager_GetMeta(t *testing.T) {
provider.getMetaFunc = func(ctx context.Context, pluginID, version string) (*Result, error) {
return &Result{
Meta: pluginsv0alpha1.PluginMetaJSONData{Id: "different"},
Meta: pluginsv0alpha1.MetaJSONData{Id: "different"},
TTL: time.Hour,
}, nil
}
@@ -73,10 +73,10 @@ func TestProviderManager_GetMeta(t *testing.T) {
})
t.Run("fetches from provider when not cached", func(t *testing.T) {
expectedMeta := pluginsv0alpha1.PluginMetaJSONData{
expectedMeta := pluginsv0alpha1.MetaJSONData{
Id: "test-plugin",
Name: "Test Plugin",
Type: pluginsv0alpha1.PluginMetaJSONDataTypeDatasource,
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
}
expectedTTL := 2 * time.Hour
@@ -108,15 +108,15 @@ func TestProviderManager_GetMeta(t *testing.T) {
})
t.Run("does not cache result with zero TTL and tries next provider", func(t *testing.T) {
zeroTTLMeta := pluginsv0alpha1.PluginMetaJSONData{
zeroTTLMeta := pluginsv0alpha1.MetaJSONData{
Id: "test-plugin",
Name: "Zero TTL Plugin",
Type: pluginsv0alpha1.PluginMetaJSONDataTypeDatasource,
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
}
expectedMeta := pluginsv0alpha1.PluginMetaJSONData{
expectedMeta := pluginsv0alpha1.MetaJSONData{
Id: "test-plugin",
Name: "Test Plugin",
Type: pluginsv0alpha1.PluginMetaJSONDataTypeDatasource,
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
}
provider1 := &mockProvider{
@@ -154,10 +154,10 @@ func TestProviderManager_GetMeta(t *testing.T) {
})
t.Run("tries next provider when first returns ErrMetaNotFound", func(t *testing.T) {
expectedMeta := pluginsv0alpha1.PluginMetaJSONData{
expectedMeta := pluginsv0alpha1.MetaJSONData{
Id: "test-plugin",
Name: "Test Plugin",
Type: pluginsv0alpha1.PluginMetaJSONDataTypeDatasource,
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
}
provider1 := &mockProvider{
@@ -229,15 +229,15 @@ func TestProviderManager_GetMeta(t *testing.T) {
})
t.Run("skips expired cache entries", func(t *testing.T) {
expiredMeta := pluginsv0alpha1.PluginMetaJSONData{
expiredMeta := pluginsv0alpha1.MetaJSONData{
Id: "test-plugin",
Name: "Expired Plugin",
Type: pluginsv0alpha1.PluginMetaJSONDataTypeDatasource,
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
}
expectedMeta := pluginsv0alpha1.PluginMetaJSONData{
expectedMeta := pluginsv0alpha1.MetaJSONData{
Id: "test-plugin",
Name: "Test Plugin",
Type: pluginsv0alpha1.PluginMetaJSONDataTypeDatasource,
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
}
callCount := 0
@@ -272,15 +272,15 @@ func TestProviderManager_GetMeta(t *testing.T) {
})
t.Run("uses first successful provider", func(t *testing.T) {
expectedMeta1 := pluginsv0alpha1.PluginMetaJSONData{
expectedMeta1 := pluginsv0alpha1.MetaJSONData{
Id: "test-plugin",
Name: "Provider 1 Plugin",
Type: pluginsv0alpha1.PluginMetaJSONDataTypeDatasource,
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
}
expectedMeta2 := pluginsv0alpha1.PluginMetaJSONData{
expectedMeta2 := pluginsv0alpha1.MetaJSONData{
Id: "test-plugin",
Name: "Provider 2 Plugin",
Type: pluginsv0alpha1.PluginMetaJSONDataTypeDatasource,
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
}
provider1 := &mockProvider{
@@ -331,9 +331,9 @@ func TestProviderManager_Run(t *testing.T) {
func TestProviderManager_cleanupExpired(t *testing.T) {
t.Run("removes expired entries", func(t *testing.T) {
validMeta := pluginsv0alpha1.PluginMetaJSONData{Id: "valid"}
expiredMeta1 := pluginsv0alpha1.PluginMetaJSONData{Id: "expired1"}
expiredMeta2 := pluginsv0alpha1.PluginMetaJSONData{Id: "expired2"}
validMeta := pluginsv0alpha1.MetaJSONData{Id: "valid"}
expiredMeta1 := pluginsv0alpha1.MetaJSONData{Id: "expired1"}
expiredMeta2 := pluginsv0alpha1.MetaJSONData{Id: "expired2"}
provider := &mockProvider{
getMetaFunc: func(ctx context.Context, pluginID, version string) (*Result, error) {
+2 -2
View File
@@ -14,14 +14,14 @@ var (
// Result contains plugin metadata along with its recommended TTL.
type Result struct {
Meta pluginsv0alpha1.PluginMetaJSONData
Meta pluginsv0alpha1.MetaJSONData
TTL time.Duration
}
// Provider is used for retrieving plugin metadata.
type Provider interface {
// GetMeta retrieves plugin metadata for the given plugin ID and version.
// Returns the Result containing the PluginMetaJSONData and its recommended TTL.
// Returns the Result containing the MetaJSONData and its recommended TTL.
// If the plugin is not found, returns ErrMetaNotFound.
GetMeta(ctx context.Context, pluginID, version string) (*Result, error)
}
+34 -34
View File
@@ -22,15 +22,15 @@ import (
)
var (
_ rest.Scoper = (*PluginMetaStorage)(nil)
_ rest.SingularNameProvider = (*PluginMetaStorage)(nil)
_ rest.Getter = (*PluginMetaStorage)(nil)
_ rest.Lister = (*PluginMetaStorage)(nil)
_ rest.Storage = (*PluginMetaStorage)(nil)
_ rest.TableConvertor = (*PluginMetaStorage)(nil)
_ rest.Scoper = (*MetaStorage)(nil)
_ rest.SingularNameProvider = (*MetaStorage)(nil)
_ rest.Getter = (*MetaStorage)(nil)
_ rest.Lister = (*MetaStorage)(nil)
_ rest.Storage = (*MetaStorage)(nil)
_ rest.TableConvertor = (*MetaStorage)(nil)
)
type PluginMetaStorage struct {
type MetaStorage struct {
metaManager *meta.ProviderManager
client *pluginsv0alpha1.PluginClient
clientFactory func(context.Context) (*pluginsv0alpha1.PluginClient, error)
@@ -41,16 +41,16 @@ type PluginMetaStorage struct {
tableConverter rest.TableConvertor
}
func NewPluginMetaStorage(
func NewMetaStorage(
metaManager *meta.ProviderManager,
clientFactory func(context.Context) (*pluginsv0alpha1.PluginClient, error),
) *PluginMetaStorage {
) *MetaStorage {
gr := schema.GroupResource{
Group: pluginsv0alpha1.APIGroup,
Resource: strings.ToLower(pluginsv0alpha1.PluginMetaKind().Plural()),
Resource: strings.ToLower(pluginsv0alpha1.MetaKind().Plural()),
}
return &PluginMetaStorage{
return &MetaStorage{
metaManager: metaManager,
clientFactory: clientFactory,
gr: gr,
@@ -58,7 +58,7 @@ func NewPluginMetaStorage(
}
}
func (s *PluginMetaStorage) getClient(ctx context.Context) (*pluginsv0alpha1.PluginClient, error) {
func (s *MetaStorage) getClient(ctx context.Context) (*pluginsv0alpha1.PluginClient, error) {
s.clientOnce.Do(func() {
client, err := s.clientFactory(ctx)
if err != nil {
@@ -72,29 +72,29 @@ func (s *PluginMetaStorage) getClient(ctx context.Context) (*pluginsv0alpha1.Plu
return s.client, s.clientErr
}
func (s *PluginMetaStorage) New() runtime.Object {
return pluginsv0alpha1.PluginMetaKind().ZeroValue()
func (s *MetaStorage) New() runtime.Object {
return pluginsv0alpha1.MetaKind().ZeroValue()
}
func (s *PluginMetaStorage) Destroy() {}
func (s *MetaStorage) Destroy() {}
func (s *PluginMetaStorage) NamespaceScoped() bool {
func (s *MetaStorage) NamespaceScoped() bool {
return true
}
func (s *PluginMetaStorage) GetSingularName() string {
return strings.ToLower(pluginsv0alpha1.PluginMetaKind().Kind())
func (s *MetaStorage) GetSingularName() string {
return strings.ToLower(pluginsv0alpha1.MetaKind().Kind())
}
func (s *PluginMetaStorage) NewList() runtime.Object {
return pluginsv0alpha1.PluginMetaKind().ZeroListValue()
func (s *MetaStorage) NewList() runtime.Object {
return pluginsv0alpha1.MetaKind().ZeroListValue()
}
func (s *PluginMetaStorage) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
func (s *MetaStorage) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
return s.tableConverter.ConvertToTable(ctx, object, tableOptions)
}
func (s *PluginMetaStorage) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
func (s *MetaStorage) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
ns, err := request.NamespaceInfoFrom(ctx, true)
if err != nil {
return nil, err
@@ -111,8 +111,8 @@ func (s *PluginMetaStorage) List(ctx context.Context, options *internalversion.L
return nil, apierrors.NewInternalError(fmt.Errorf("failed to list plugins: %w", err))
}
// Convert each Plugin to PluginMeta
metaItems := make([]pluginsv0alpha1.PluginMeta, 0, len(plugins.Items))
// Convert each Plugin to Meta
metaItems := make([]pluginsv0alpha1.Meta, 0, len(plugins.Items))
for _, plugin := range plugins.Items {
result, err := s.metaManager.GetMeta(ctx, plugin.Spec.Id, plugin.Spec.Version)
if err != nil {
@@ -121,14 +121,14 @@ func (s *PluginMetaStorage) List(ctx context.Context, options *internalversion.L
continue
}
pluginMeta := createPluginMetaFromPluginMetaJSONData(result.Meta, plugin.Name, plugin.Namespace)
pluginMeta := createMetaFromMetaJSONData(result.Meta, plugin.Name, plugin.Namespace)
metaItems = append(metaItems, *pluginMeta)
}
list := &pluginsv0alpha1.PluginMetaList{
list := &pluginsv0alpha1.MetaList{
TypeMeta: metav1.TypeMeta{
APIVersion: pluginsv0alpha1.APIGroup + "/" + pluginsv0alpha1.APIVersion,
Kind: pluginsv0alpha1.PluginMetaKind().Kind() + "List",
Kind: pluginsv0alpha1.MetaKind().Kind() + "List",
},
Items: metaItems,
}
@@ -136,7 +136,7 @@ func (s *PluginMetaStorage) List(ctx context.Context, options *internalversion.L
return list, nil
}
func (s *PluginMetaStorage) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
func (s *MetaStorage) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
ns, err := request.NamespaceInfoFrom(ctx, true)
if err != nil {
return nil, err
@@ -169,17 +169,17 @@ func (s *PluginMetaStorage) Get(ctx context.Context, name string, options *metav
return nil, apierrors.NewInternalError(fmt.Errorf("failed to fetch plugin metadata: %w", err))
}
return createPluginMetaFromPluginMetaJSONData(result.Meta, name, ns.Value), nil
return createMetaFromMetaJSONData(result.Meta, name, ns.Value), nil
}
// createPluginMetaFromPluginMetaJSONData creates a PluginMeta k8s object from PluginMetaJSONData and plugin metadata.
func createPluginMetaFromPluginMetaJSONData(pluginJSON pluginsv0alpha1.PluginMetaJSONData, name, namespace string) *pluginsv0alpha1.PluginMeta {
pluginMeta := &pluginsv0alpha1.PluginMeta{
// createMetaFromMetaJSONData creates a Meta k8s object from MetaJSONData and plugin metadata.
func createMetaFromMetaJSONData(pluginJSON pluginsv0alpha1.MetaJSONData, name, namespace string) *pluginsv0alpha1.Meta {
pluginMeta := &pluginsv0alpha1.Meta{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Spec: pluginsv0alpha1.PluginMetaSpec{
Spec: pluginsv0alpha1.MetaSpec{
PluginJSON: pluginJSON,
},
}
@@ -188,7 +188,7 @@ func createPluginMetaFromPluginMetaJSONData(pluginJSON pluginsv0alpha1.PluginMet
pluginMeta.SetGroupVersionKind(schema.GroupVersionKind{
Group: pluginsv0alpha1.APIGroup,
Version: pluginsv0alpha1.APIVersion,
Kind: pluginsv0alpha1.PluginMetaKind().Kind(),
Kind: pluginsv0alpha1.MetaKind().Kind(),
})
return pluginMeta
+2 -3
View File
@@ -1747,9 +1747,8 @@ enabled = true
#################################### Short Links #############################
[short_links]
# Short links that are never accessed will be deleted as cleanup. Time is set up in days. The default is 7 days. Maximum value is 365.
# 0 means they will be deleted approximately every 10 minutes. A negative value (such as -1) will disable expiration.
expire_time = 7
# Short links that are never accessed will be deleted as cleanup. Time is set up in days. The default is -1 (never expire). Maximum value is 365.
expire_time = -1
#################################### Internal Grafana Metrics ############
# Metrics available at HTTP URL /metrics and /metrics/plugins/:pluginId
+2 -2
View File
@@ -1689,8 +1689,8 @@ default_datasource_uid =
#################################### Short Links #############################
[short_links]
# Short links which are never accessed will be deleted as cleanup. Time is in days. Default is 7 days. Max is 365. 0 means they will be deleted approximately every 10 minutes.
;expire_time = 7
# Short links that are never accessed will be deleted as cleanup. Time is set up in days. The default is -1 (never expire). Maximum value is 365.
;expire_time = -1
#################################### Internal Grafana Metrics ##########################
# Metrics available at HTTP URL /metrics and /metrics/plugins/:pluginId
@@ -2142,17 +2142,13 @@ Configures settings around the short link feature.
#### `expire_time`
Short links that are never accessed are considered expired or stale and are deleted as cleanup.
Short links that are never accessed are considered expired or stale and can be deleted as cleanup.
Set the expiration time in days.
The default is `7` days.
The default is `-1` days (never expire).
The maximum is `365` days.
A setting above the maximum uses the value `365` instead.
Setting `0` means the short links are cleaned up approximately every 10 minutes.
A negative value such as `-1` disables expiry.
{{< admonition type="caution" >}}
Short links without an expiration increase the size of the database and can't be deleted. Grafana recommends setting a duration based on your specific use case
{{< /admonition >}}
A setting above the maximum uses the value `365` instead.
A negative value such as `-1` disables expiry.
<hr>
+1
View File
@@ -250,6 +250,7 @@ require (
github.com/grafana/grafana/apps/plugins v0.0.0 // @grafana/plugins-platform-backend
github.com/grafana/grafana/apps/preferences v0.0.0 // @grafana/grafana-app-platform-squad
github.com/grafana/grafana/apps/provisioning v0.0.0 // @grafana/grafana-app-platform-squad
github.com/grafana/grafana/apps/quotas v0.0.0-20251209183543-1013d74f13f2 // @grafana/grafana-search-and-storage
github.com/grafana/grafana/apps/scope v0.0.0 // @grafana/grafana-operator-experience-squad
github.com/grafana/grafana/apps/secret v0.0.0 // @grafana/grafana-operator-experience-squad
github.com/grafana/grafana/apps/shorturl v0.0.0 // @grafana/sharing-squad
+4
View File
@@ -646,6 +646,7 @@ gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0p
github.com/1NCE-GmbH/grpc-go-pool v0.0.0-20231117122434-2a5bb974daa2 h1:qFYgLH2zZe3WHpQgUrzeazC+ebDebwAQqS9yE1cP5Bs=
github.com/1NCE-GmbH/grpc-go-pool v0.0.0-20231117122434-2a5bb974daa2/go.mod h1:09/ALd1AXCTCOfcJYD8+jIYKmFmi6PVCkTsipC18F7E=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U=
github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=
github.com/Azure/azure-sdk-for-go v23.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
@@ -1074,6 +1075,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/cznic/b v0.0.0-20180115125044-35e9bbe41f07/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8=
github.com/cznic/fileutil v0.0.0-20180108211300-6a051e75936f/go.mod h1:8S58EK26zhXSxzv7NQFpnliaOQsmDUxvoQO3rt154Vg=
github.com/cznic/golex v0.0.0-20170803123110-4ab7c5e190e4/go.mod h1:+bmmJDNmKlhWNG+gwWCkaBoTy39Fs+bzRxVBzoTQbIc=
@@ -1651,6 +1653,8 @@ github.com/grafana/grafana-plugin-sdk-go v0.284.0 h1:1bK7eWsnPBLUWDcWJWe218Ik5ad
github.com/grafana/grafana-plugin-sdk-go v0.284.0/go.mod h1:lHPniaSxq3SL5MxDIPy04TYB1jnTp/ivkYO+xn5Rz3E=
github.com/grafana/grafana/apps/example v0.0.0-20251027162426-edef69fdc82b h1:6Bo65etvjQ4tStkaA5+N3A3ENbO4UAWj53TxF6g2Hdk=
github.com/grafana/grafana/apps/example v0.0.0-20251027162426-edef69fdc82b/go.mod h1:6+wASOCN8LWt6FJ8dc0oODUBIEY5XHaE6ABi8g0mR+k=
github.com/grafana/grafana/apps/quotas v0.0.0-20251209183543-1013d74f13f2 h1:rDPMdshj3QMvpXn+wK4T8awF9n2sd8i4YRiGqX2xTvg=
github.com/grafana/grafana/apps/quotas v0.0.0-20251209183543-1013d74f13f2/go.mod h1:M7bV60iRB61y0ISPG1HX/oNLZtlh0ZF22rUYwNkAKjo=
github.com/grafana/grafana/pkg/promlib v0.0.8 h1:VUWsqttdf0wMI4j9OX9oNrykguQpZcruudDAFpJJVw0=
github.com/grafana/grafana/pkg/promlib v0.0.8/go.mod h1:U1ezG/MGaEPoThqsr3lymMPN5yIPdVTJnDZ+wcXT+ao=
github.com/grafana/grafana/pkg/semconv v0.0.0-20250804150913-990f1c69ecc2 h1:A65jWgLk4Re28gIuZcpC0aTh71JZ0ey89hKGE9h543s=
+1 -2
View File
@@ -56,8 +56,7 @@ import (
_ "github.com/grafana/e2e"
_ "github.com/grafana/gofpdf"
_ "github.com/grafana/gomemcache/memcache"
_ "github.com/grafana/tempo/pkg/traceql"
_ "github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1"
_ "github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1"
_ "github.com/grafana/tempo/pkg/traceql"
)
+11
View File
@@ -53,11 +53,22 @@ func validateOnCreate(ctx context.Context, f *folders.Folder, getter parentsGett
return folder.ErrFolderCannotBeParentOfItself
}
// note: `parents` will include itself as the last item
parents, err := getter(ctx, f)
if err != nil {
return fmt.Errorf("unable to create folder inside parent: %w", err)
}
for i, parent := range parents.Items {
// skip the last item, which is itself
if i == len(parents.Items)-1 {
continue
}
if parent.Name == f.Name {
return folder.ErrCircularReference.Errorf("circular reference detected")
}
}
// Can not create a folder that will be too deep.
// We need to add +1 as we also have the root folder as part of the parents.
if len(parents.Items) > maxDepth+1 {
@@ -127,6 +127,27 @@ func TestValidateCreate(t *testing.T) {
},
maxDepth: folder.MaxNestedFolderDepth,
},
{
name: "cannot create a circular reference",
folder: &folders.Folder{
ObjectMeta: metav1.ObjectMeta{
Name: "3",
Annotations: map[string]string{"grafana.app/folder": "2"},
},
Spec: folders.FolderSpec{
Title: "some title",
},
},
expectedErr: "circular reference detected",
getter: &folders.FolderInfoList{
Items: []folders.FolderInfo{
{Name: "2", Parent: "1"},
{Name: "1", Parent: "3"},
{Name: "3", Parent: folder.GeneralFolderUID},
{Name: folder.GeneralFolderUID},
},
},
},
}
for _, tt := range tests {
+6 -6
View File
@@ -90,11 +90,11 @@ func RegisterAPIService(
builder := &IdentityAccessManagementAPIBuilder{
store: store,
userLegacyStore: user.NewLegacyStore(store, accessClient, enableAuthnMutation),
saLegacyStore: serviceaccount.NewLegacyStore(store, accessClient, enableAuthnMutation),
legacyTeamStore: team.NewLegacyStore(store, legacyAccessClient, enableAuthnMutation),
teamBindingLegacyStore: teambinding.NewLegacyBindingStore(store, enableAuthnMutation),
ssoLegacyStore: sso.NewLegacyStore(ssoService),
userLegacyStore: user.NewLegacyStore(store, accessClient, enableAuthnMutation, tracing),
saLegacyStore: serviceaccount.NewLegacyStore(store, accessClient, enableAuthnMutation, tracing),
legacyTeamStore: team.NewLegacyStore(store, legacyAccessClient, enableAuthnMutation, tracing),
teamBindingLegacyStore: teambinding.NewLegacyBindingStore(store, enableAuthnMutation, tracing),
ssoLegacyStore: sso.NewLegacyStore(ssoService, tracing),
coreRolesStorage: coreRolesStorage,
rolesStorage: rolesStorage,
resourcePermissionsStorage: resourcepermission.ProvideStorageBackend(dbProvider),
@@ -114,7 +114,7 @@ func RegisterAPIService(
dual: dual,
unified: unified,
userSearchClient: resource.NewSearchClient(dualwrite.NewSearchAdapter(dual), iamv0.UserResourceInfo.GroupResource(),
unified, user.NewUserLegacySearchClient(userService), features),
unified, user.NewUserLegacySearchClient(userService, tracing), features),
teamSearch: NewTeamSearchHandler(tracing, dual, team.NewLegacyTeamSearchClient(teamService), unified, features),
}
apiregistration.RegisterAPI(builder)
+16 -2
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"strings"
"go.opentelemetry.io/otel/trace"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -35,14 +36,15 @@ var (
var resource = iamv0alpha1.ServiceAccountResourceInfo
func NewLegacyStore(store legacy.LegacyIdentityStore, ac claims.AccessClient, enableAuthnMutation bool) *LegacyStore {
return &LegacyStore{store, ac, enableAuthnMutation}
func NewLegacyStore(store legacy.LegacyIdentityStore, ac claims.AccessClient, enableAuthnMutation bool, tracer trace.Tracer) *LegacyStore {
return &LegacyStore{store, ac, enableAuthnMutation, tracer}
}
type LegacyStore struct {
store legacy.LegacyIdentityStore
ac claims.AccessClient
enableAuthnMutation bool
tracer trace.Tracer
}
// DeleteCollection implements rest.CollectionDeleter.
@@ -52,6 +54,9 @@ func (s *LegacyStore) DeleteCollection(ctx context.Context, deleteValidation res
// Delete implements rest.GracefulDeleter.
func (s *LegacyStore) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
ctx, span := s.tracer.Start(ctx, "serviceaccount.Delete")
defer span.End()
if !s.enableAuthnMutation {
return nil, false, apierrors.NewMethodNotSupported(resource.GroupResource(), "delete")
}
@@ -95,6 +100,9 @@ func (s *LegacyStore) Update(ctx context.Context, name string, objInfo rest.Upda
// Create implements rest.Creater.
func (s *LegacyStore) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
ctx, span := s.tracer.Start(ctx, "serviceaccount.Create")
defer span.End()
if !s.enableAuthnMutation {
return nil, apierrors.NewMethodNotSupported(resource.GroupResource(), "create")
}
@@ -165,6 +173,9 @@ func (s *LegacyStore) ConvertToTable(ctx context.Context, object runtime.Object,
}
func (s *LegacyStore) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
ctx, span := s.tracer.Start(ctx, "serviceaccount.List")
defer span.End()
res, err := common.List(
ctx, resource, s.ac, common.PaginationFromListOptions(options),
func(ctx context.Context, ns claims.NamespaceInfo, p common.Pagination) (*common.ListResponse[iamv0alpha1.ServiceAccount], error) {
@@ -228,6 +239,9 @@ func extractPluginNameFromTitle(title string) string {
}
func (s *LegacyStore) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
ctx, span := s.tracer.Start(ctx, "serviceaccount.Get")
defer span.End()
ns, err := request.NamespaceInfoFrom(ctx, true)
if err != nil {
return nil, err
+16 -2
View File
@@ -7,6 +7,7 @@ import (
commonv1 "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"go.opentelemetry.io/otel/trace"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -32,12 +33,13 @@ var (
var resource = iamv0.SSOSettingResourceInfo
func NewLegacyStore(service ssosettings.Service) *LegacyStore {
return &LegacyStore{service}
func NewLegacyStore(service ssosettings.Service, tracer trace.Tracer) *LegacyStore {
return &LegacyStore{service, tracer}
}
type LegacyStore struct {
service ssosettings.Service
tracer trace.Tracer
}
// Destroy implements rest.Storage.
@@ -71,6 +73,9 @@ func (s *LegacyStore) NewList() runtime.Object {
// List implements rest.Lister.
func (s *LegacyStore) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
ctx, span := s.tracer.Start(ctx, "sso.List")
defer span.End()
ns, _ := request.NamespaceInfoFrom(ctx, false)
settings, err := s.service.List(ctx)
@@ -88,6 +93,9 @@ func (s *LegacyStore) List(ctx context.Context, options *internalversion.ListOpt
// Get implements rest.Getter.
func (s *LegacyStore) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
ctx, span := s.tracer.Start(ctx, "sso.Get")
defer span.End()
ns, _ := request.NamespaceInfoFrom(ctx, false)
setting, err := s.service.GetForProviderWithRedactedSecrets(ctx, name)
@@ -112,6 +120,9 @@ func (s *LegacyStore) Update(
_ bool,
_ *metav1.UpdateOptions,
) (runtime.Object, bool, error) {
ctx, span := s.tracer.Start(ctx, "sso.Update")
defer span.End()
const created = false
ident, err := identity.GetRequester(ctx)
if err != nil {
@@ -148,6 +159,9 @@ func (s *LegacyStore) Delete(
_ rest.ValidateObjectFunc,
options *metav1.DeleteOptions,
) (runtime.Object, bool, error) {
ctx, span := s.tracer.Start(ctx, "sso.Delete")
defer span.End()
obj, err := s.Get(ctx, name, nil)
if err != nil {
return obj, false, err
+19 -2
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"strconv"
"go.opentelemetry.io/otel/trace"
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
@@ -35,14 +36,15 @@ var (
var resource = iamv0alpha1.TeamResourceInfo
func NewLegacyStore(store legacy.LegacyIdentityStore, ac claims.AccessClient, enableAuthnMutation bool) *LegacyStore {
return &LegacyStore{store, ac, enableAuthnMutation}
func NewLegacyStore(store legacy.LegacyIdentityStore, ac claims.AccessClient, enableAuthnMutation bool, tracer trace.Tracer) *LegacyStore {
return &LegacyStore{store, ac, enableAuthnMutation, tracer}
}
type LegacyStore struct {
store legacy.LegacyIdentityStore
ac claims.AccessClient
enableAuthnMutation bool
tracer trace.Tracer
}
func (s *LegacyStore) New() runtime.Object {
@@ -74,6 +76,9 @@ func (s *LegacyStore) DeleteCollection(ctx context.Context, deleteValidation res
// Delete implements rest.GracefulDeleter.
func (s *LegacyStore) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
ctx, span := s.tracer.Start(ctx, "team.Delete")
defer span.End()
if !s.enableAuthnMutation {
return nil, false, apierrors.NewMethodNotSupported(resource.GroupResource(), "delete")
}
@@ -112,6 +117,9 @@ func (s *LegacyStore) Delete(ctx context.Context, name string, deleteValidation
// Update implements rest.Updater.
func (s *LegacyStore) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
ctx, span := s.tracer.Start(ctx, "team.Update")
defer span.End()
if !s.enableAuthnMutation {
return nil, false, apierrors.NewMethodNotSupported(resource.GroupResource(), "update")
}
@@ -161,6 +169,9 @@ func (s *LegacyStore) Update(ctx context.Context, name string, objInfo rest.Upda
}
func (s *LegacyStore) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
ctx, span := s.tracer.Start(ctx, "team.List")
defer span.End()
res, err := common.List(
ctx, resource, s.ac, common.PaginationFromListOptions(options),
func(ctx context.Context, ns claims.NamespaceInfo, p common.Pagination) (*common.ListResponse[iamv0alpha1.Team], error) {
@@ -197,6 +208,9 @@ func (s *LegacyStore) List(ctx context.Context, options *internalversion.ListOpt
}
func (s *LegacyStore) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
ctx, span := s.tracer.Start(ctx, "team.Get")
defer span.End()
ns, err := request.NamespaceInfoFrom(ctx, true)
if err != nil {
return nil, err
@@ -219,6 +233,9 @@ func (s *LegacyStore) Get(ctx context.Context, name string, options *metav1.GetO
}
func (s *LegacyStore) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
ctx, span := s.tracer.Start(ctx, "team.Create")
defer span.End()
if !s.enableAuthnMutation {
return nil, apierrors.NewMethodNotSupported(resource.GroupResource(), "create")
}
+19 -2
View File
@@ -6,6 +6,7 @@ import (
"strconv"
"time"
"go.opentelemetry.io/otel/trace"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -35,13 +36,14 @@ var (
_ rest.CollectionDeleter = (*LegacyBindingStore)(nil)
)
func NewLegacyBindingStore(store legacy.LegacyIdentityStore, enableAuthnMutation bool) *LegacyBindingStore {
return &LegacyBindingStore{store, enableAuthnMutation}
func NewLegacyBindingStore(store legacy.LegacyIdentityStore, enableAuthnMutation bool, tracer trace.Tracer) *LegacyBindingStore {
return &LegacyBindingStore{store, enableAuthnMutation, tracer}
}
type LegacyBindingStore struct {
store legacy.LegacyIdentityStore
enableAuthnMutation bool
tracer trace.Tracer
}
// Destroy implements rest.Storage.
@@ -73,6 +75,9 @@ func (l *LegacyBindingStore) ConvertToTable(ctx context.Context, object runtime.
}
func (l *LegacyBindingStore) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
ctx, span := l.tracer.Start(ctx, "teambinding.Update")
defer span.End()
if !l.enableAuthnMutation {
return nil, false, apierrors.NewMethodNotSupported(bindingResource.GroupResource(), "update")
}
@@ -125,6 +130,9 @@ func (l *LegacyBindingStore) Update(ctx context.Context, name string, objInfo re
}
func (l *LegacyBindingStore) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
ctx, span := l.tracer.Start(ctx, "teambinding.Delete")
defer span.End()
if !l.enableAuthnMutation {
return nil, false, apierrors.NewMethodNotSupported(bindingResource.GroupResource(), "delete")
}
@@ -160,6 +168,9 @@ func (l *LegacyBindingStore) DeleteCollection(ctx context.Context, deleteValidat
}
func (l *LegacyBindingStore) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
ctx, span := l.tracer.Start(ctx, "teambinding.Create")
defer span.End()
if !l.enableAuthnMutation {
return nil, apierrors.NewMethodNotSupported(bindingResource.GroupResource(), "create")
}
@@ -230,6 +241,9 @@ func (l *LegacyBindingStore) Create(ctx context.Context, obj runtime.Object, cre
// Get implements rest.Getter.
func (l *LegacyBindingStore) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
ctx, span := l.tracer.Start(ctx, "teambinding.Get")
defer span.End()
ns, err := request.NamespaceInfoFrom(ctx, true)
if err != nil {
return nil, err
@@ -254,6 +268,9 @@ func (l *LegacyBindingStore) Get(ctx context.Context, name string, options *meta
// List implements rest.Lister.
func (l *LegacyBindingStore) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
ctx, span := l.tracer.Start(ctx, "teambinding.List")
defer span.End()
ns, err := request.NamespaceInfoFrom(ctx, true)
if err != nil {
return nil, err
+7 -1
View File
@@ -6,6 +6,7 @@ import (
"log/slog"
"math"
"go.opentelemetry.io/otel/trace"
"google.golang.org/grpc"
"github.com/grafana/grafana/pkg/apimachinery/identity"
@@ -27,13 +28,15 @@ type UserLegacySearchClient struct {
resourcepb.ResourceIndexClient
userService user.Service
log *slog.Logger
tracer trace.Tracer
}
// NewUserLegacySearchClient creates a new UserLegacySearchClient.
func NewUserLegacySearchClient(userService user.Service) *UserLegacySearchClient {
func NewUserLegacySearchClient(userService user.Service, tracer trace.Tracer) *UserLegacySearchClient {
return &UserLegacySearchClient{
userService: userService,
log: slog.Default().With("logger", "legacy-user-search-client"),
tracer: tracer,
}
}
@@ -41,6 +44,9 @@ func NewUserLegacySearchClient(userService user.Service) *UserLegacySearchClient
// It only supports exact matching for title, login, or email.
// FIXME: This implementation only supports a single field query and will be extended in the future.
func (c *UserLegacySearchClient) Search(ctx context.Context, req *resourcepb.ResourceSearchRequest, _ ...grpc.CallOption) (*resourcepb.ResourceSearchResponse, error) {
ctx, span := c.tracer.Start(ctx, "user.Search")
defer span.End()
signedInUser, err := identity.GetRequester(ctx)
if err != nil {
return nil, err
@@ -8,6 +8,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/services/user/usertest"
res "github.com/grafana/grafana/pkg/storage/unified/resource"
@@ -17,7 +18,7 @@ import (
func TestUserLegacySearchClient_Search(t *testing.T) {
t.Run("should return error if no query fields are provided", func(t *testing.T) {
mockUserService := usertest.NewMockService(t)
client := NewUserLegacySearchClient(mockUserService)
client := NewUserLegacySearchClient(mockUserService, tracing.NewNoopTracerService())
ctx := identity.WithRequester(context.Background(), &user.SignedInUser{OrgID: 1, UserID: 1})
req := &resourcepb.ResourceSearchRequest{
Options: &resourcepb.ListOptions{
@@ -66,7 +67,7 @@ func TestUserLegacySearchClient_Search(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
mockUserService := usertest.NewMockService(t)
client := NewUserLegacySearchClient(mockUserService)
client := NewUserLegacySearchClient(mockUserService, tracing.NewNoopTracerService())
ctx := identity.WithRequester(context.Background(), &user.SignedInUser{OrgID: 1, UserID: 1})
req := &resourcepb.ResourceSearchRequest{
Limit: 10,
@@ -125,7 +126,7 @@ func TestUserLegacySearchClient_Search(t *testing.T) {
t.Run("title should have precedence over login and email", func(t *testing.T) {
mockUserService := usertest.NewMockService(t)
client := NewUserLegacySearchClient(mockUserService)
client := NewUserLegacySearchClient(mockUserService, tracing.NewNoopTracerService())
ctx := identity.WithRequester(context.Background(), &user.SignedInUser{OrgID: 1, UserID: 1})
req := &resourcepb.ResourceSearchRequest{
Options: &resourcepb.ListOptions{
+19 -2
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"time"
"go.opentelemetry.io/otel/trace"
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
@@ -36,18 +37,22 @@ var (
var resource = iamv0alpha1.UserResourceInfo
func NewLegacyStore(store legacy.LegacyIdentityStore, ac claims.AccessClient, enableAuthnMutation bool) *LegacyStore {
return &LegacyStore{store, ac, enableAuthnMutation}
func NewLegacyStore(store legacy.LegacyIdentityStore, ac claims.AccessClient, enableAuthnMutation bool, tracer trace.Tracer) *LegacyStore {
return &LegacyStore{store, ac, enableAuthnMutation, tracer}
}
type LegacyStore struct {
store legacy.LegacyIdentityStore
ac claims.AccessClient
enableAuthnMutation bool
tracer trace.Tracer
}
// Update implements rest.Updater.
func (s *LegacyStore) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
ctx, span := s.tracer.Start(ctx, "user.Update")
defer span.End()
if !s.enableAuthnMutation {
return nil, false, apierrors.NewMethodNotSupported(resource.GroupResource(), "update")
}
@@ -105,6 +110,9 @@ func (s *LegacyStore) DeleteCollection(ctx context.Context, deleteValidation res
// Delete implements rest.GracefulDeleter.
func (s *LegacyStore) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
ctx, span := s.tracer.Start(ctx, "user.Delete")
defer span.End()
if !s.enableAuthnMutation {
return nil, false, apierrors.NewMethodNotSupported(resource.GroupResource(), "delete")
}
@@ -171,6 +179,9 @@ func (s *LegacyStore) ConvertToTable(ctx context.Context, object runtime.Object,
}
func (s *LegacyStore) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
ctx, span := s.tracer.Start(ctx, "user.List")
defer span.End()
res, err := common.List(
ctx, resource, s.ac, common.PaginationFromListOptions(options),
func(ctx context.Context, ns claims.NamespaceInfo, p common.Pagination) (*common.ListResponse[iamv0alpha1.User], error) {
@@ -206,6 +217,9 @@ func (s *LegacyStore) List(ctx context.Context, options *internalversion.ListOpt
}
func (s *LegacyStore) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
ctx, span := s.tracer.Start(ctx, "user.Get")
defer span.End()
ns, err := request.NamespaceInfoFrom(ctx, true)
if err != nil {
return nil, err
@@ -229,6 +243,9 @@ func (s *LegacyStore) Get(ctx context.Context, name string, options *metav1.GetO
// Create implements rest.Creater.
func (s *LegacyStore) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
ctx, span := s.tracer.Start(ctx, "user.Create")
defer span.End()
if !s.enableAuthnMutation {
return nil, apierrors.NewMethodNotSupported(resource.GroupResource(), "create")
}
+7 -7
View File
@@ -13,15 +13,15 @@ const (
ActionPluginsPluginsDelete = "plugins.plugins:delete" // DELETE.
// PluginMetas
ActionPluginsPluginsMetaCreate = "plugins.pluginsmeta:create" // CREATE.
ActionPluginsPluginsMetaWrite = "plugins.pluginsmeta:write" // UPDATE.
ActionPluginsPluginsMetaRead = "plugins.pluginsmeta:read" // GET + LIST.
ActionPluginsPluginsMetaDelete = "plugins.pluginsmeta:delete" // DELETE.
ActionPluginsPluginsMetaCreate = "plugins.metas:create" // CREATE.
ActionPluginsPluginsMetaWrite = "plugins.metas:write" // UPDATE.
ActionPluginsPluginsMetaRead = "plugins.metas:read" // GET + LIST.
ActionPluginsPluginsMetaDelete = "plugins.metas:delete" // DELETE.
)
var (
ScopeProviderPluginsPlugins = accesscontrol.NewScopeProvider("plugins.plugins")
ScopeProviderPluginsPluginsMeta = accesscontrol.NewScopeProvider("plugins.pluginsmeta")
ScopeProviderPluginsPluginsMeta = accesscontrol.NewScopeProvider("plugins.metas")
ScopeAllPluginsPlugins = ScopeProviderPluginsPlugins.GetResourceAllScope()
ScopeAllPluginsPluginsMeta = ScopeProviderPluginsPluginsMeta.GetResourceAllScope()
@@ -76,7 +76,7 @@ func registerAccessControlRoles(service accesscontrol.Service) error {
// PluginMetas
pluginsMetaReader := accesscontrol.RoleRegistration{
Role: accesscontrol.RoleDTO{
Name: "fixed:plugins.pluginsmeta:reader",
Name: "fixed:plugins.metas:reader",
DisplayName: "Plugin Metas Reader",
Description: "Read and list plugin metadata.",
Group: "Plugins",
@@ -92,7 +92,7 @@ func registerAccessControlRoles(service accesscontrol.Service) error {
pluginsMetaWriter := accesscontrol.RoleRegistration{
Role: accesscontrol.RoleDTO{
Name: "fixed:plugins.pluginsmeta:writer",
Name: "fixed:plugins.metas:writer",
DisplayName: "Plugin Metas Writer",
Description: "Create, update and delete plugin metadata.",
Group: "Plugins",
@@ -85,7 +85,7 @@ func newPermissionRegistry() *permissionRegistry {
"orgs": "orgs:id:",
"plugins": "plugins:id:",
"plugins.plugins": "plugins.plugins:uid:",
"plugins.pluginsmeta": "plugins.pluginsmeta:uid:",
"plugins.metas": "plugins.metas:uid:",
"provisioners": "provisioners:",
"reports": "reports:id:",
"permissions": "permissions:type:",
+2 -2
View File
@@ -298,8 +298,8 @@ func NewMapperRegistry() MapperRegistry {
},
},
"plugins.grafana.app": {
"plugins": newResourceTranslation("plugins.plugins", "uid", false, nil),
"pluginsmeta": newResourceTranslation("plugins.pluginsmeta", "uid", false, nil),
"plugins": newResourceTranslation("plugins.plugins", "uid", false, nil),
"metas": newResourceTranslation("plugins.metas", "uid", false, nil),
},
})
+1 -1
View File
@@ -1330,7 +1330,7 @@ func (cfg *Cfg) parseINIFile(iniFile *ini.File) error {
cfg.QueryHistoryEnabled = queryHistory.Key("enabled").MustBool(true)
shortLinks := iniFile.Section("short_links")
cfg.ShortLinkExpiration = shortLinks.Key("expire_time").MustInt(7)
cfg.ShortLinkExpiration = shortLinks.Key("expire_time").MustInt(-1)
if cfg.ShortLinkExpiration > 365 {
cfg.Logger.Warn("short_links expire_time must be less than 366 days. Setting to 365 days")
+7
View File
@@ -1618,6 +1618,7 @@ func (s *server) checkQuota(ctx context.Context, nsr NamespacedResource) {
))
if s.overridesService == nil {
s.log.FromContext(ctx).Debug("overrides service not configured, skipping quota check", "namespace", nsr.Namespace, "group", nsr.Group, "resource", nsr.Resource)
return
}
@@ -1632,6 +1633,12 @@ func (s *server) checkQuota(ctx context.Context, nsr NamespacedResource) {
s.log.FromContext(ctx).Error("failed to get resource stats for quota checking", "namespace", nsr.Namespace, "group", nsr.Group, "resource", nsr.Resource, "error", err)
return
}
if len(stats) > 0 {
s.log.FromContext(ctx).Debug("stats found", "namespace", nsr.Namespace, "group", nsr.Group, "resource", nsr.Resource, "count", stats[0].Count)
} else {
s.log.FromContext(ctx).Debug("no stats found for resource", "namespace", nsr.Namespace, "group", nsr.Group, "resource", nsr.Resource)
}
if len(stats) > 0 && stats[0].Count >= int64(quota.Limit) {
s.log.FromContext(ctx).Info("Quota exceeded on create", "namespace", nsr.Namespace, "group", nsr.Group, "resource", nsr.Resource, "quota", quota.Limit, "count", stats[0].Count, "stats_resource", stats[0].Resource)
}
+4 -4
View File
@@ -15,19 +15,19 @@ const pluginsDiscoveryJSON = `[
"freshness": "Current",
"resources": [
{
"resource": "pluginmetas",
"resource": "metas",
"responseKind": {
"group": "",
"kind": "PluginMeta",
"kind": "Meta",
"version": ""
},
"scope": "Namespaced",
"singularResource": "pluginmeta",
"singularResource": "meta",
"subresources": [
{
"responseKind": {
"group": "",
"kind": "PluginMeta",
"kind": "Meta",
"version": ""
},
"subresource": "status",
+4 -4
View File
@@ -21,19 +21,19 @@ func TestIntegrationPluginsIntegrationDiscovery(t *testing.T) {
"freshness": "Current",
"resources": [
{
"resource": "pluginmetas",
"resource": "metas",
"responseKind": {
"group": "",
"kind": "PluginMeta",
"kind": "Meta",
"version": ""
},
"scope": "Namespaced",
"singularResource": "pluginmeta",
"singularResource": "meta",
"subresources": [
{
"responseKind": {
"group": "",
"kind": "PluginMeta",
"kind": "Meta",
"version": ""
},
"subresource": "status",
@@ -15,6 +15,72 @@ import (
func TestIntegrationPluginMeta(t *testing.T) {
testutil.SkipIntegrationTestInShortMode(t)
t.Run("list plugin metas", func(t *testing.T) {
helper := setupHelper(t)
ctx := context.Background()
client := helper.GetResourceClient(apis.ResourceClientArgs{
User: helper.Org1.Admin,
GVR: gvrPlugins,
})
plugin1Name := "test-plugin-metas-1"
plugin1 := helper.LoadYAMLOrJSON(fmt.Sprintf(`{
"apiVersion": "plugins.grafana.app/v0alpha1",
"kind": "Plugin",
"metadata": {"name": "%s"},
"spec": {"id": "grafana-piechart-panel", "version": "1.0.0"}
}`, plugin1Name))
_, err := client.Resource.Create(ctx, plugin1, metav1.CreateOptions{})
require.NoError(t, err)
plugin2Name := "test-plugin-metas-2"
plugin2 := helper.LoadYAMLOrJSON(fmt.Sprintf(`{
"apiVersion": "plugins.grafana.app/v0alpha1",
"kind": "Plugin",
"metadata": {"name": "%s"},
"spec": {"id": "grafana-clock-panel", "version": "1.0.0"}
}`, plugin2Name))
_, err = client.Resource.Create(ctx, plugin2, metav1.CreateOptions{})
require.NoError(t, err)
namespace := helper.Namespacer(helper.Org1.Admin.Identity.GetOrgID())
path := fmt.Sprintf("/apis/plugins.grafana.app/v0alpha1/namespaces/%s/metas", namespace)
response := apis.DoRequest(helper, apis.RequestParams{
User: helper.Org1.Admin,
Method: "GET",
Path: path,
}, &pluginsv0alpha1.MetaList{})
require.NotNil(t, response.Result)
require.NotNil(t, response.Result.Items)
require.GreaterOrEqual(t, len(response.Result.Items), 2)
foundIDs := make(map[string]bool)
for _, item := range response.Result.Items {
require.NotNil(t, item.Spec.PluginJSON)
foundIDs[item.Spec.PluginJSON.Id] = true
require.NotEmpty(t, item.Spec.PluginJSON.Id)
require.NotEmpty(t, item.Spec.PluginJSON.Type)
require.NotEmpty(t, item.Spec.PluginJSON.Name)
}
require.True(t, foundIDs["grafana-piechart-panel"])
require.True(t, foundIDs["grafana-clock-panel"])
})
t.Run("list plugin metas with no plugins", func(t *testing.T) {
helper := setupHelper(t)
namespace := helper.Namespacer(helper.Org1.Admin.Identity.GetOrgID())
path := fmt.Sprintf("/apis/plugins.grafana.app/v0alpha1/namespaces/%s/metas", namespace)
response := apis.DoRequest(helper, apis.RequestParams{
User: helper.Org1.Admin,
Method: "GET",
Path: path,
}, &pluginsv0alpha1.MetaList{})
require.NotNil(t, response.Result)
require.NotNil(t, response.Result.Items)
require.GreaterOrEqual(t, len(response.Result.Items), 0)
})
t.Run("get plugin meta", func(t *testing.T) {
helper := setupHelper(t)
@@ -35,12 +101,12 @@ func TestIntegrationPluginMeta(t *testing.T) {
require.NoError(t, err)
namespace := helper.Org1.Admin.Identity.GetNamespace()
path := fmt.Sprintf("/apis/plugins.grafana.app/v0alpha1/namespaces/%s/pluginmetas/%s", namespace, pluginName)
path := fmt.Sprintf("/apis/plugins.grafana.app/v0alpha1/namespaces/%s/metas/%s", namespace, pluginName)
response := apis.DoRequest(helper, apis.RequestParams{
User: helper.Org1.Admin,
Method: "GET",
Path: path,
}, &pluginsv0alpha1.PluginMeta{})
}, &pluginsv0alpha1.Meta{})
require.NotNil(t, response.Result)
require.NotNil(t, response.Result.Spec.PluginJSON)
@@ -52,12 +118,12 @@ func TestIntegrationPluginMeta(t *testing.T) {
t.Run("get plugin meta for non-existent plugin", func(t *testing.T) {
helper := setupHelper(t)
namespace := helper.Org1.Admin.Identity.GetNamespace()
path := fmt.Sprintf("/apis/plugins.grafana.app/v0alpha1/namespaces/%s/pluginmetas/non-existent-plugin", namespace)
path := fmt.Sprintf("/apis/plugins.grafana.app/v0alpha1/namespaces/%s/metas/non-existent-plugin", namespace)
response := apis.DoRequest(helper, apis.RequestParams{
User: helper.Org1.Admin,
Method: "GET",
Path: path,
}, &pluginsv0alpha1.PluginMeta{})
}, &pluginsv0alpha1.Meta{})
require.NotNil(t, response.Status)
require.Equal(t, int32(404), response.Status.Code)
@@ -82,12 +148,12 @@ func TestIntegrationPluginMeta(t *testing.T) {
require.NoError(t, err)
namespace := helper.Org1.Admin.Identity.GetNamespace()
path := fmt.Sprintf("/apis/plugins.grafana.app/v0alpha1/namespaces/%s/pluginmetas/%s", namespace, pluginName)
path := fmt.Sprintf("/apis/plugins.grafana.app/v0alpha1/namespaces/%s/metas/%s", namespace, pluginName)
response := apis.DoRequest(helper, apis.RequestParams{
User: helper.Org1.Admin,
Method: "GET",
Path: path,
}, &pluginsv0alpha1.PluginMeta{})
}, &pluginsv0alpha1.Meta{})
require.NotNil(t, response.Status)
require.Equal(t, int32(404), response.Status.Code)
@@ -1,85 +0,0 @@
package plugins
import (
"context"
"fmt"
"testing"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
pluginsv0alpha1 "github.com/grafana/grafana/apps/plugins/pkg/apis/plugins/v0alpha1"
"github.com/grafana/grafana/pkg/tests/apis"
"github.com/grafana/grafana/pkg/util/testutil"
)
func TestIntegrationPluginMetas(t *testing.T) {
testutil.SkipIntegrationTestInShortMode(t)
t.Run("list plugin metas", func(t *testing.T) {
helper := setupHelper(t)
ctx := context.Background()
client := helper.GetResourceClient(apis.ResourceClientArgs{
User: helper.Org1.Admin,
GVR: gvrPlugins,
})
plugin1Name := "test-plugin-metas-1"
plugin1 := helper.LoadYAMLOrJSON(fmt.Sprintf(`{
"apiVersion": "plugins.grafana.app/v0alpha1",
"kind": "Plugin",
"metadata": {"name": "%s"},
"spec": {"id": "grafana-piechart-panel", "version": "1.0.0"}
}`, plugin1Name))
_, err := client.Resource.Create(ctx, plugin1, metav1.CreateOptions{})
require.NoError(t, err)
plugin2Name := "test-plugin-metas-2"
plugin2 := helper.LoadYAMLOrJSON(fmt.Sprintf(`{
"apiVersion": "plugins.grafana.app/v0alpha1",
"kind": "Plugin",
"metadata": {"name": "%s"},
"spec": {"id": "grafana-clock-panel", "version": "1.0.0"}
}`, plugin2Name))
_, err = client.Resource.Create(ctx, plugin2, metav1.CreateOptions{})
require.NoError(t, err)
namespace := helper.Namespacer(helper.Org1.Admin.Identity.GetOrgID())
path := fmt.Sprintf("/apis/plugins.grafana.app/v0alpha1/namespaces/%s/pluginmetas", namespace)
response := apis.DoRequest(helper, apis.RequestParams{
User: helper.Org1.Admin,
Method: "GET",
Path: path,
}, &pluginsv0alpha1.PluginMetaList{})
require.NotNil(t, response.Result)
require.NotNil(t, response.Result.Items)
require.GreaterOrEqual(t, len(response.Result.Items), 2)
foundIDs := make(map[string]bool)
for _, item := range response.Result.Items {
require.NotNil(t, item.Spec.PluginJSON)
foundIDs[item.Spec.PluginJSON.Id] = true
require.NotEmpty(t, item.Spec.PluginJSON.Id)
require.NotEmpty(t, item.Spec.PluginJSON.Type)
require.NotEmpty(t, item.Spec.PluginJSON.Name)
}
require.True(t, foundIDs["grafana-piechart-panel"])
require.True(t, foundIDs["grafana-clock-panel"])
})
t.Run("list plugin metas with no plugins", func(t *testing.T) {
helper := setupHelper(t)
namespace := helper.Namespacer(helper.Org1.Admin.Identity.GetOrgID())
path := fmt.Sprintf("/apis/plugins.grafana.app/v0alpha1/namespaces/%s/pluginmetas", namespace)
response := apis.DoRequest(helper, apis.RequestParams{
User: helper.Org1.Admin,
Method: "GET",
Path: path,
}, &pluginsv0alpha1.PluginMetaList{})
require.NotNil(t, response.Result)
require.NotNil(t, response.Result.Items)
require.GreaterOrEqual(t, len(response.Result.Items), 0)
})
}
@@ -236,6 +236,10 @@ export class KeybindingSrv {
appEvents.publish(new ZoomOutEvent({ scale: 0.5, updateUrl }));
});
this.bind('t =', () => {
appEvents.publish(new ZoomOutEvent({ scale: 0.5, updateUrl }));
});
this.bind('t -', () => {
appEvents.publish(new ZoomOutEvent({ scale: 2, updateUrl }));
});
@@ -269,6 +269,13 @@ describe('setupKeyboardShortcuts', () => {
expect(tPlusBinding).toBeDefined();
});
it('should setup t = zoom in shortcut', () => {
setupKeyboardShortcuts(mockScene);
const tEqualsBinding = mockKeybindingSet.addBinding.mock.calls.find((call) => call[0].key === 't =');
expect(tEqualsBinding).toBeDefined();
});
it('should setup t - zoom out shortcut with keypress type', () => {
setupKeyboardShortcuts(mockScene);
@@ -302,9 +309,11 @@ describe('setupKeyboardShortcuts', () => {
setupKeyboardShortcuts(mockScene);
const tPlusBinding = mockKeybindingSet.addBinding.mock.calls.find((call) => call[0].key === 't +');
const tEqualsBinding = mockKeybindingSet.addBinding.mock.calls.find((call) => call[0].key === 't =');
const tMinusBinding = mockKeybindingSet.addBinding.mock.calls.find((call) => call[0].key === 't -');
expect(tPlusBinding).toBeUndefined();
expect(tEqualsBinding).toBeUndefined();
expect(tMinusBinding).toBeUndefined();
});
});
@@ -364,6 +373,28 @@ describe('setupKeyboardShortcuts', () => {
expect(newSpan).toBe(3 * 60 * 60 * 1000); // 3 hours in milliseconds
});
it('should zoom in (scale 0.5) when t = is pressed', () => {
setupKeyboardShortcuts(mockScene);
const tEqualsBinding = mockKeybindingSet.addBinding.mock.calls.find((call) => call[0].key === 't =');
const handler = tEqualsBinding![0].onTrigger;
handler();
// Scale 0.5 should result in 3 hour span (half of 6)
expect(mockTimeRange.onTimeRangeChange).toHaveBeenCalledWith(
expect.objectContaining({
from: expect.any(Object),
to: expect.any(Object),
raw: expect.any(Object),
})
);
const call = mockTimeRange.onTimeRangeChange.mock.calls[0][0];
const newSpan = call.to.valueOf() - call.from.valueOf();
expect(newSpan).toBe(3 * 60 * 60 * 1000); // 3 hours in milliseconds
});
it('should keep center point when zooming in', () => {
setupKeyboardShortcuts(mockScene);
@@ -138,6 +138,13 @@ export function setupKeyboardShortcuts(scene: DashboardScene) {
},
});
keybindings.addBinding({
key: 't =',
onTrigger: () => {
handleZoom(scene, 0.5);
},
});
keybindings.addBinding({
key: 't -',
type: 'keypress', // NOTE: Because some browsers/OS identify minus symbol differently.