mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Introduce Comparator interface (#88016)
* Introduce Comparator interface * Add compare implementation everywhere * Add comment explaining what Compare should do * Lint
This commit is contained in:
parent
06304894a1
commit
ade96dbdbd
@ -573,6 +573,8 @@ github.com/grafana/grafana-plugin-sdk-go v0.230.0 h1:Y4IL+eT1jXqTCctlNzdCvxAozpB
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.230.0/go.mod h1:6V6ikT4ryva8MrAp7Bdz5fTJx3/ztzKvpMJFfpzr4CI=
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.231.1-0.20240523124942-62dae9836284/go.mod h1:bNgmNmub1I7Mc8dzIncgNqHC5jTgSZPPHlZ3aG8HKJQ=
|
||||
github.com/grafana/grafana/pkg/promlib v0.0.3/go.mod h1:3El4NlsfALz8QQCbEGHGFvJUG+538QLMuALRhZ3pcoo=
|
||||
github.com/grafana/saml v0.4.15-0.20231025143828-a6c0e9b86a4c h1:1pHLC1ZTz7N5QI3jzCs5sqmVvAKe+JwGnpp9lQ+iUjY=
|
||||
github.com/grafana/saml v0.4.15-0.20231025143828-a6c0e9b86a4c/go.mod h1:S4+611dxnKt8z/ulbvaJzcgSHsuhjVc1QHNTcr1R7Fw=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1/go.mod h1:YvJ2f6MplWDhfxiUC3KpyTy76kYUZA4W3pTv/wdKQ9Y=
|
||||
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU=
|
||||
|
@ -35,6 +35,8 @@ type Storage interface {
|
||||
rest.CreaterUpdater
|
||||
rest.GracefulDeleter
|
||||
rest.CollectionDeleter
|
||||
// Compare asserts on the equality of objects returned from both stores (object storage and legacy storage)
|
||||
Compare(storageObj, legacyObj runtime.Object) bool
|
||||
}
|
||||
|
||||
// LegacyStorage is a storage implementation that writes to the Grafana SQL database.
|
||||
@ -91,18 +93,18 @@ func NewDualWriter(mode DualWriterMode, legacy LegacyStorage, storage Storage) D
|
||||
switch mode {
|
||||
case Mode1:
|
||||
// read and write only from legacy storage
|
||||
return NewDualWriterMode1(legacy, storage)
|
||||
return newDualWriterMode1(legacy, storage)
|
||||
case Mode2:
|
||||
// write to both, read from storage but use legacy as backup
|
||||
return NewDualWriterMode2(legacy, storage)
|
||||
return newDualWriterMode2(legacy, storage)
|
||||
case Mode3:
|
||||
// write to both, read from storage only
|
||||
return NewDualWriterMode3(legacy, storage)
|
||||
return newDualWriterMode3(legacy, storage)
|
||||
case Mode4:
|
||||
// read and write only from storage
|
||||
return NewDualWriterMode4(legacy, storage)
|
||||
return newDualWriterMode4(legacy, storage)
|
||||
default:
|
||||
return NewDualWriterMode1(legacy, storage)
|
||||
return newDualWriterMode1(legacy, storage)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ const mode1Str = "1"
|
||||
|
||||
// NewDualWriterMode1 returns a new DualWriter in mode 1.
|
||||
// Mode 1 represents writing to and reading from LegacyStorage.
|
||||
func NewDualWriterMode1(legacy LegacyStorage, storage Storage) *DualWriterMode1 {
|
||||
func newDualWriterMode1(legacy LegacyStorage, storage Storage) *DualWriterMode1 {
|
||||
metrics := &dualWriterMetrics{}
|
||||
metrics.init()
|
||||
return &DualWriterMode1{Legacy: legacy, Storage: storage, Log: klog.NewKlogr().WithName("DualWriterMode1"), dualWriterMetrics: metrics}
|
||||
@ -236,3 +236,7 @@ func (d *DualWriterMode1) NewList() runtime.Object {
|
||||
func (d *DualWriterMode1) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
|
||||
return d.Legacy.ConvertToTable(ctx, object, tableOptions)
|
||||
}
|
||||
|
||||
func (d *DualWriterMode1) Compare(storageObj, legacyObj runtime.Object) bool {
|
||||
return d.Storage.Compare(storageObj, legacyObj)
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ const mode2Str = "2"
|
||||
|
||||
// NewDualWriterMode2 returns a new DualWriter in mode 2.
|
||||
// Mode 2 represents writing to LegacyStorage and Storage and reading from LegacyStorage.
|
||||
func NewDualWriterMode2(legacy LegacyStorage, storage Storage) *DualWriterMode2 {
|
||||
func newDualWriterMode2(legacy LegacyStorage, storage Storage) *DualWriterMode2 {
|
||||
metrics := &dualWriterMetrics{}
|
||||
metrics.init()
|
||||
return &DualWriterMode2{Legacy: legacy, Storage: storage, Log: klog.NewKlogr().WithName("DualWriterMode2"), dualWriterMetrics: metrics}
|
||||
@ -310,6 +310,10 @@ func (d *DualWriterMode2) ConvertToTable(ctx context.Context, object runtime.Obj
|
||||
return d.Storage.ConvertToTable(ctx, object, tableOptions)
|
||||
}
|
||||
|
||||
func (d *DualWriterMode2) Compare(storageObj, legacyObj runtime.Object) bool {
|
||||
return d.Storage.Compare(storageObj, legacyObj)
|
||||
}
|
||||
|
||||
func parseList(legacyList []runtime.Object) (metainternalversion.ListOptions, map[string]int, error) {
|
||||
options := metainternalversion.ListOptions{}
|
||||
originKeys := []string{}
|
||||
|
@ -18,9 +18,9 @@ type DualWriterMode3 struct {
|
||||
Log klog.Logger
|
||||
}
|
||||
|
||||
// NewDualWriterMode3 returns a new DualWriter in mode 3.
|
||||
// newDualWriterMode3 returns a new DualWriter in mode 3.
|
||||
// Mode 3 represents writing to LegacyStorage and Storage and reading from Storage.
|
||||
func NewDualWriterMode3(legacy LegacyStorage, storage Storage) *DualWriterMode3 {
|
||||
func newDualWriterMode3(legacy LegacyStorage, storage Storage) *DualWriterMode3 {
|
||||
metrics := &dualWriterMetrics{}
|
||||
metrics.init()
|
||||
return &DualWriterMode3{Legacy: legacy, Storage: storage, Log: klog.NewKlogr().WithName("DualWriterMode3"), dualWriterMetrics: metrics}
|
||||
@ -157,3 +157,7 @@ func (d *DualWriterMode3) NewList() runtime.Object {
|
||||
func (d *DualWriterMode3) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
|
||||
return d.Storage.ConvertToTable(ctx, object, tableOptions)
|
||||
}
|
||||
|
||||
func (d *DualWriterMode3) Compare(storageObj, legacyObj runtime.Object) bool {
|
||||
return d.Storage.Compare(storageObj, legacyObj)
|
||||
}
|
||||
|
@ -17,9 +17,9 @@ type DualWriterMode4 struct {
|
||||
Log klog.Logger
|
||||
}
|
||||
|
||||
// NewDualWriterMode4 returns a new DualWriter in mode 4.
|
||||
// newDualWriterMode4 returns a new DualWriter in mode 4.
|
||||
// Mode 4 represents writing and reading from Storage.
|
||||
func NewDualWriterMode4(legacy LegacyStorage, storage Storage) *DualWriterMode4 {
|
||||
func newDualWriterMode4(legacy LegacyStorage, storage Storage) *DualWriterMode4 {
|
||||
metrics := &dualWriterMetrics{}
|
||||
metrics.init()
|
||||
return &DualWriterMode4{Legacy: legacy, Storage: storage, Log: klog.NewKlogr().WithName("DualWriterMode4"), dualWriterMetrics: metrics}
|
||||
@ -85,3 +85,7 @@ func (d *DualWriterMode4) NewList() runtime.Object {
|
||||
func (d *DualWriterMode4) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
|
||||
return d.Storage.ConvertToTable(ctx, object, tableOptions)
|
||||
}
|
||||
|
||||
func (d *DualWriterMode4) Compare(storageObj, legacyObj runtime.Object) bool {
|
||||
return d.Storage.Compare(storageObj, legacyObj)
|
||||
}
|
||||
|
@ -1,15 +1,11 @@
|
||||
package dashboard
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||
common "k8s.io/kube-openapi/pkg/common"
|
||||
@ -24,7 +20,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/registry/apis/dashboard/access"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/utils"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
@ -119,43 +114,10 @@ func (b *DashboardsAPIBuilder) GetAPIGroupInfo(
|
||||
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(v0alpha1.GROUP, scheme, metav1.ParameterCodec, codecs)
|
||||
|
||||
resourceInfo := v0alpha1.DashboardResourceInfo
|
||||
strategy := grafanaregistry.NewStrategy(scheme)
|
||||
store := &genericregistry.Store{
|
||||
NewFunc: resourceInfo.NewFunc,
|
||||
NewListFunc: resourceInfo.NewListFunc,
|
||||
PredicateFunc: grafanaregistry.Matcher,
|
||||
DefaultQualifiedResource: resourceInfo.GroupResource(),
|
||||
SingularQualifiedResource: resourceInfo.SingularGroupResource(),
|
||||
CreateStrategy: strategy,
|
||||
UpdateStrategy: strategy,
|
||||
DeleteStrategy: strategy,
|
||||
store, err := newStorage(scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
store.TableConvertor = utils.NewTableConverter(
|
||||
store.DefaultQualifiedResource,
|
||||
[]metav1.TableColumnDefinition{
|
||||
{Name: "Name", Type: "string", Format: "name"},
|
||||
{Name: "Title", Type: "string", Format: "string", Description: "The dashboard name"},
|
||||
{Name: "Created At", Type: "date"},
|
||||
},
|
||||
func(obj any) ([]interface{}, error) {
|
||||
dash, ok := obj.(*v0alpha1.Dashboard)
|
||||
if ok {
|
||||
return []interface{}{
|
||||
dash.Name,
|
||||
dash.Spec.GetNestedString("title"),
|
||||
dash.CreationTimestamp.UTC().Format(time.RFC3339),
|
||||
}, nil
|
||||
}
|
||||
summary, ok := obj.(*v0alpha1.DashboardSummary)
|
||||
if ok {
|
||||
return []interface{}{
|
||||
dash.Name,
|
||||
summary.Spec.Title,
|
||||
dash.CreationTimestamp.UTC().Format(time.RFC3339),
|
||||
}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("expected dashboard or summary")
|
||||
})
|
||||
|
||||
legacyStore := &dashboardStorage{
|
||||
resource: resourceInfo,
|
||||
|
72
pkg/registry/apis/dashboard/storage.go
Normal file
72
pkg/registry/apis/dashboard/storage.go
Normal file
@ -0,0 +1,72 @@
|
||||
package dashboard
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/utils"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
|
||||
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
|
||||
)
|
||||
|
||||
var _ grafanarest.Storage = (*storage)(nil)
|
||||
|
||||
type storage struct {
|
||||
*genericregistry.Store
|
||||
}
|
||||
|
||||
func newStorage(scheme *runtime.Scheme) (*storage, error) {
|
||||
strategy := grafanaregistry.NewStrategy(scheme)
|
||||
resourceInfo := v0alpha1.DashboardResourceInfo
|
||||
store := &genericregistry.Store{
|
||||
NewFunc: resourceInfo.NewFunc,
|
||||
NewListFunc: resourceInfo.NewListFunc,
|
||||
PredicateFunc: grafanaregistry.Matcher,
|
||||
DefaultQualifiedResource: resourceInfo.GroupResource(),
|
||||
SingularQualifiedResource: resourceInfo.SingularGroupResource(),
|
||||
CreateStrategy: strategy,
|
||||
UpdateStrategy: strategy,
|
||||
DeleteStrategy: strategy,
|
||||
}
|
||||
|
||||
store.TableConvertor = utils.NewTableConverter(
|
||||
store.DefaultQualifiedResource,
|
||||
[]metav1.TableColumnDefinition{
|
||||
{Name: "Name", Type: "string", Format: "name"},
|
||||
{Name: "Title", Type: "string", Format: "string", Description: "The dashboard name"},
|
||||
{Name: "Created At", Type: "date"},
|
||||
},
|
||||
func(obj any) ([]interface{}, error) {
|
||||
dash, ok := obj.(*v0alpha1.Dashboard)
|
||||
if ok {
|
||||
if dash != nil {
|
||||
return []interface{}{
|
||||
dash.Name,
|
||||
dash.Spec.GetNestedString("title"),
|
||||
dash.CreationTimestamp.UTC().Format(time.RFC3339),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
summary, ok := obj.(*v0alpha1.DashboardSummary)
|
||||
if ok {
|
||||
return []interface{}{
|
||||
dash.Name,
|
||||
summary.Spec.Title,
|
||||
dash.CreationTimestamp.UTC().Format(time.RFC3339),
|
||||
}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("expected dashboard or summary")
|
||||
})
|
||||
return &storage{Store: store}, nil
|
||||
}
|
||||
|
||||
// Compare asserts on the equality of objects returned from both stores (object storage and legacy storage)
|
||||
func (s *storage) Compare(storageObj, legacyObj runtime.Object) bool {
|
||||
//TODO: define the comparison logic between a dashboard returned by the storage and a dashboard returned by the legacy storage
|
||||
return false
|
||||
}
|
@ -38,3 +38,9 @@ func newStorage(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter, le
|
||||
}
|
||||
return &storage{Store: store}, nil
|
||||
}
|
||||
|
||||
// Compare asserts on the equality of objects returned from both stores (object storage and legacy storage)
|
||||
func (s *storage) Compare(storageObj, legacyObj runtime.Object) bool {
|
||||
//TODO: define the comparison logic between a folder returned by the storage and a folder returned by the legacy storage
|
||||
return false
|
||||
}
|
||||
|
@ -60,3 +60,9 @@ func newStorage(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter) (*
|
||||
}
|
||||
return &storage{Store: store}, nil
|
||||
}
|
||||
|
||||
// Compare asserts on the equality of objects returned from both stores (object storage and legacy storage)
|
||||
func (s *storage) Compare(storageObj, legacyObj runtime.Object) bool {
|
||||
//TODO: define the comparison logic between a query template returned by the storage and a query template returned by the legacy storage
|
||||
return false
|
||||
}
|
||||
|
@ -38,3 +38,9 @@ func newStorage(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter, le
|
||||
}
|
||||
return &storage{Store: store}, nil
|
||||
}
|
||||
|
||||
// Compare asserts on the equality of objects returned from both stores (object storage and legacy storage)
|
||||
func (s *storage) Compare(storageObj, legacyObj runtime.Object) bool {
|
||||
//TODO: define the comparison logic between a playlist returned by the storage and a playlist returned by the legacy storage
|
||||
return false
|
||||
}
|
||||
|
@ -184,3 +184,9 @@ func SelectableScopeNodeFields(obj *scope.ScopeNode) fields.Set {
|
||||
"spec.parentName": parentName,
|
||||
})
|
||||
}
|
||||
|
||||
// Compare asserts on the equality of objects returned from both stores (object storage and legacy storage)
|
||||
func (s *storage) Compare(storageObj, legacyObj runtime.Object) bool {
|
||||
//TODO: define the comparison logic between a scope returned by the storage and a scope returned by the legacy storage
|
||||
return false
|
||||
}
|
||||
|
@ -60,3 +60,9 @@ func newStorage(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter) (*
|
||||
}
|
||||
return &storage{Store: store}, nil
|
||||
}
|
||||
|
||||
// Compare asserts on the equality of objects returned from both stores (object storage and legacy storage)
|
||||
func (s *storage) Compare(storageObj, legacyObj runtime.Object) bool {
|
||||
//TODO: define the comparison logic between a generic object returned by the storage and a generic object returned by the legacy storage
|
||||
return false
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user