Skip to content

Commit ade96db

Browse files
Introduce Comparator interface (grafana#88016)
* Introduce Comparator interface * Add compare implementation everywhere * Add comment explaining what Compare should do * Lint
1 parent 0630489 commit ade96db

File tree

13 files changed

+136
-52
lines changed

13 files changed

+136
-52
lines changed

go.work.sum

+2
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,8 @@ github.com/grafana/grafana-plugin-sdk-go v0.230.0 h1:Y4IL+eT1jXqTCctlNzdCvxAozpB
573573
github.com/grafana/grafana-plugin-sdk-go v0.230.0/go.mod h1:6V6ikT4ryva8MrAp7Bdz5fTJx3/ztzKvpMJFfpzr4CI=
574574
github.com/grafana/grafana-plugin-sdk-go v0.231.1-0.20240523124942-62dae9836284/go.mod h1:bNgmNmub1I7Mc8dzIncgNqHC5jTgSZPPHlZ3aG8HKJQ=
575575
github.com/grafana/grafana/pkg/promlib v0.0.3/go.mod h1:3El4NlsfALz8QQCbEGHGFvJUG+538QLMuALRhZ3pcoo=
576+
github.com/grafana/saml v0.4.15-0.20231025143828-a6c0e9b86a4c h1:1pHLC1ZTz7N5QI3jzCs5sqmVvAKe+JwGnpp9lQ+iUjY=
577+
github.com/grafana/saml v0.4.15-0.20231025143828-a6c0e9b86a4c/go.mod h1:S4+611dxnKt8z/ulbvaJzcgSHsuhjVc1QHNTcr1R7Fw=
576578
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
577579
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1/go.mod h1:YvJ2f6MplWDhfxiUC3KpyTy76kYUZA4W3pTv/wdKQ9Y=
578580
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU=

pkg/apiserver/rest/dualwriter.go

+7-5
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ type Storage interface {
3535
rest.CreaterUpdater
3636
rest.GracefulDeleter
3737
rest.CollectionDeleter
38+
// Compare asserts on the equality of objects returned from both stores (object storage and legacy storage)
39+
Compare(storageObj, legacyObj runtime.Object) bool
3840
}
3941

4042
// 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
9193
switch mode {
9294
case Mode1:
9395
// read and write only from legacy storage
94-
return NewDualWriterMode1(legacy, storage)
96+
return newDualWriterMode1(legacy, storage)
9597
case Mode2:
9698
// write to both, read from storage but use legacy as backup
97-
return NewDualWriterMode2(legacy, storage)
99+
return newDualWriterMode2(legacy, storage)
98100
case Mode3:
99101
// write to both, read from storage only
100-
return NewDualWriterMode3(legacy, storage)
102+
return newDualWriterMode3(legacy, storage)
101103
case Mode4:
102104
// read and write only from storage
103-
return NewDualWriterMode4(legacy, storage)
105+
return newDualWriterMode4(legacy, storage)
104106
default:
105-
return NewDualWriterMode1(legacy, storage)
107+
return newDualWriterMode1(legacy, storage)
106108
}
107109
}
108110

pkg/apiserver/rest/dualwriter_mode1.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const mode1Str = "1"
2323

2424
// NewDualWriterMode1 returns a new DualWriter in mode 1.
2525
// Mode 1 represents writing to and reading from LegacyStorage.
26-
func NewDualWriterMode1(legacy LegacyStorage, storage Storage) *DualWriterMode1 {
26+
func newDualWriterMode1(legacy LegacyStorage, storage Storage) *DualWriterMode1 {
2727
metrics := &dualWriterMetrics{}
2828
metrics.init()
2929
return &DualWriterMode1{Legacy: legacy, Storage: storage, Log: klog.NewKlogr().WithName("DualWriterMode1"), dualWriterMetrics: metrics}
@@ -236,3 +236,7 @@ func (d *DualWriterMode1) NewList() runtime.Object {
236236
func (d *DualWriterMode1) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
237237
return d.Legacy.ConvertToTable(ctx, object, tableOptions)
238238
}
239+
240+
func (d *DualWriterMode1) Compare(storageObj, legacyObj runtime.Object) bool {
241+
return d.Storage.Compare(storageObj, legacyObj)
242+
}

pkg/apiserver/rest/dualwriter_mode2.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const mode2Str = "2"
2727

2828
// NewDualWriterMode2 returns a new DualWriter in mode 2.
2929
// Mode 2 represents writing to LegacyStorage and Storage and reading from LegacyStorage.
30-
func NewDualWriterMode2(legacy LegacyStorage, storage Storage) *DualWriterMode2 {
30+
func newDualWriterMode2(legacy LegacyStorage, storage Storage) *DualWriterMode2 {
3131
metrics := &dualWriterMetrics{}
3232
metrics.init()
3333
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
310310
return d.Storage.ConvertToTable(ctx, object, tableOptions)
311311
}
312312

313+
func (d *DualWriterMode2) Compare(storageObj, legacyObj runtime.Object) bool {
314+
return d.Storage.Compare(storageObj, legacyObj)
315+
}
316+
313317
func parseList(legacyList []runtime.Object) (metainternalversion.ListOptions, map[string]int, error) {
314318
options := metainternalversion.ListOptions{}
315319
originKeys := []string{}

pkg/apiserver/rest/dualwriter_mode3.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ type DualWriterMode3 struct {
1818
Log klog.Logger
1919
}
2020

21-
// NewDualWriterMode3 returns a new DualWriter in mode 3.
21+
// newDualWriterMode3 returns a new DualWriter in mode 3.
2222
// Mode 3 represents writing to LegacyStorage and Storage and reading from Storage.
23-
func NewDualWriterMode3(legacy LegacyStorage, storage Storage) *DualWriterMode3 {
23+
func newDualWriterMode3(legacy LegacyStorage, storage Storage) *DualWriterMode3 {
2424
metrics := &dualWriterMetrics{}
2525
metrics.init()
2626
return &DualWriterMode3{Legacy: legacy, Storage: storage, Log: klog.NewKlogr().WithName("DualWriterMode3"), dualWriterMetrics: metrics}
@@ -157,3 +157,7 @@ func (d *DualWriterMode3) NewList() runtime.Object {
157157
func (d *DualWriterMode3) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
158158
return d.Storage.ConvertToTable(ctx, object, tableOptions)
159159
}
160+
161+
func (d *DualWriterMode3) Compare(storageObj, legacyObj runtime.Object) bool {
162+
return d.Storage.Compare(storageObj, legacyObj)
163+
}

pkg/apiserver/rest/dualwriter_mode4.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ type DualWriterMode4 struct {
1717
Log klog.Logger
1818
}
1919

20-
// NewDualWriterMode4 returns a new DualWriter in mode 4.
20+
// newDualWriterMode4 returns a new DualWriter in mode 4.
2121
// Mode 4 represents writing and reading from Storage.
22-
func NewDualWriterMode4(legacy LegacyStorage, storage Storage) *DualWriterMode4 {
22+
func newDualWriterMode4(legacy LegacyStorage, storage Storage) *DualWriterMode4 {
2323
metrics := &dualWriterMetrics{}
2424
metrics.init()
2525
return &DualWriterMode4{Legacy: legacy, Storage: storage, Log: klog.NewKlogr().WithName("DualWriterMode4"), dualWriterMetrics: metrics}
@@ -85,3 +85,7 @@ func (d *DualWriterMode4) NewList() runtime.Object {
8585
func (d *DualWriterMode4) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
8686
return d.Storage.ConvertToTable(ctx, object, tableOptions)
8787
}
88+
89+
func (d *DualWriterMode4) Compare(storageObj, legacyObj runtime.Object) bool {
90+
return d.Storage.Compare(storageObj, legacyObj)
91+
}

pkg/registry/apis/dashboard/register.go

+3-41
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
11
package dashboard
22

33
import (
4-
"fmt"
5-
"time"
6-
74
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
85
"k8s.io/apimachinery/pkg/runtime"
96
"k8s.io/apimachinery/pkg/runtime/schema"
107
"k8s.io/apimachinery/pkg/runtime/serializer"
118
"k8s.io/apiserver/pkg/registry/generic"
12-
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
139
"k8s.io/apiserver/pkg/registry/rest"
1410
genericapiserver "k8s.io/apiserver/pkg/server"
1511
common "k8s.io/kube-openapi/pkg/common"
@@ -24,7 +20,6 @@ import (
2420
"github.com/grafana/grafana/pkg/registry/apis/dashboard/access"
2521
"github.com/grafana/grafana/pkg/services/accesscontrol"
2622
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
27-
"github.com/grafana/grafana/pkg/services/apiserver/utils"
2823
"github.com/grafana/grafana/pkg/services/dashboards"
2924
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
3025
"github.com/grafana/grafana/pkg/services/featuremgmt"
@@ -119,43 +114,10 @@ func (b *DashboardsAPIBuilder) GetAPIGroupInfo(
119114
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(v0alpha1.GROUP, scheme, metav1.ParameterCodec, codecs)
120115

121116
resourceInfo := v0alpha1.DashboardResourceInfo
122-
strategy := grafanaregistry.NewStrategy(scheme)
123-
store := &genericregistry.Store{
124-
NewFunc: resourceInfo.NewFunc,
125-
NewListFunc: resourceInfo.NewListFunc,
126-
PredicateFunc: grafanaregistry.Matcher,
127-
DefaultQualifiedResource: resourceInfo.GroupResource(),
128-
SingularQualifiedResource: resourceInfo.SingularGroupResource(),
129-
CreateStrategy: strategy,
130-
UpdateStrategy: strategy,
131-
DeleteStrategy: strategy,
117+
store, err := newStorage(scheme)
118+
if err != nil {
119+
return nil, err
132120
}
133-
store.TableConvertor = utils.NewTableConverter(
134-
store.DefaultQualifiedResource,
135-
[]metav1.TableColumnDefinition{
136-
{Name: "Name", Type: "string", Format: "name"},
137-
{Name: "Title", Type: "string", Format: "string", Description: "The dashboard name"},
138-
{Name: "Created At", Type: "date"},
139-
},
140-
func(obj any) ([]interface{}, error) {
141-
dash, ok := obj.(*v0alpha1.Dashboard)
142-
if ok {
143-
return []interface{}{
144-
dash.Name,
145-
dash.Spec.GetNestedString("title"),
146-
dash.CreationTimestamp.UTC().Format(time.RFC3339),
147-
}, nil
148-
}
149-
summary, ok := obj.(*v0alpha1.DashboardSummary)
150-
if ok {
151-
return []interface{}{
152-
dash.Name,
153-
summary.Spec.Title,
154-
dash.CreationTimestamp.UTC().Format(time.RFC3339),
155-
}, nil
156-
}
157-
return nil, fmt.Errorf("expected dashboard or summary")
158-
})
159121

160122
legacyStore := &dashboardStorage{
161123
resource: resourceInfo,
+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package dashboard
2+
3+
import (
4+
"fmt"
5+
"time"
6+
7+
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1"
8+
"github.com/grafana/grafana/pkg/services/apiserver/utils"
9+
"k8s.io/apimachinery/pkg/runtime"
10+
11+
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
12+
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
13+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
14+
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
15+
)
16+
17+
var _ grafanarest.Storage = (*storage)(nil)
18+
19+
type storage struct {
20+
*genericregistry.Store
21+
}
22+
23+
func newStorage(scheme *runtime.Scheme) (*storage, error) {
24+
strategy := grafanaregistry.NewStrategy(scheme)
25+
resourceInfo := v0alpha1.DashboardResourceInfo
26+
store := &genericregistry.Store{
27+
NewFunc: resourceInfo.NewFunc,
28+
NewListFunc: resourceInfo.NewListFunc,
29+
PredicateFunc: grafanaregistry.Matcher,
30+
DefaultQualifiedResource: resourceInfo.GroupResource(),
31+
SingularQualifiedResource: resourceInfo.SingularGroupResource(),
32+
CreateStrategy: strategy,
33+
UpdateStrategy: strategy,
34+
DeleteStrategy: strategy,
35+
}
36+
37+
store.TableConvertor = utils.NewTableConverter(
38+
store.DefaultQualifiedResource,
39+
[]metav1.TableColumnDefinition{
40+
{Name: "Name", Type: "string", Format: "name"},
41+
{Name: "Title", Type: "string", Format: "string", Description: "The dashboard name"},
42+
{Name: "Created At", Type: "date"},
43+
},
44+
func(obj any) ([]interface{}, error) {
45+
dash, ok := obj.(*v0alpha1.Dashboard)
46+
if ok {
47+
if dash != nil {
48+
return []interface{}{
49+
dash.Name,
50+
dash.Spec.GetNestedString("title"),
51+
dash.CreationTimestamp.UTC().Format(time.RFC3339),
52+
}, nil
53+
}
54+
}
55+
summary, ok := obj.(*v0alpha1.DashboardSummary)
56+
if ok {
57+
return []interface{}{
58+
dash.Name,
59+
summary.Spec.Title,
60+
dash.CreationTimestamp.UTC().Format(time.RFC3339),
61+
}, nil
62+
}
63+
return nil, fmt.Errorf("expected dashboard or summary")
64+
})
65+
return &storage{Store: store}, nil
66+
}
67+
68+
// Compare asserts on the equality of objects returned from both stores (object storage and legacy storage)
69+
func (s *storage) Compare(storageObj, legacyObj runtime.Object) bool {
70+
//TODO: define the comparison logic between a dashboard returned by the storage and a dashboard returned by the legacy storage
71+
return false
72+
}

pkg/registry/apis/folders/storage.go

+6
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,9 @@ func newStorage(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter, le
3838
}
3939
return &storage{Store: store}, nil
4040
}
41+
42+
// Compare asserts on the equality of objects returned from both stores (object storage and legacy storage)
43+
func (s *storage) Compare(storageObj, legacyObj runtime.Object) bool {
44+
//TODO: define the comparison logic between a folder returned by the storage and a folder returned by the legacy storage
45+
return false
46+
}

pkg/registry/apis/peakq/storage.go

+6
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,9 @@ func newStorage(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter) (*
6060
}
6161
return &storage{Store: store}, nil
6262
}
63+
64+
// Compare asserts on the equality of objects returned from both stores (object storage and legacy storage)
65+
func (s *storage) Compare(storageObj, legacyObj runtime.Object) bool {
66+
//TODO: define the comparison logic between a query template returned by the storage and a query template returned by the legacy storage
67+
return false
68+
}

pkg/registry/apis/playlist/storage.go

+6
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,9 @@ func newStorage(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter, le
3838
}
3939
return &storage{Store: store}, nil
4040
}
41+
42+
// Compare asserts on the equality of objects returned from both stores (object storage and legacy storage)
43+
func (s *storage) Compare(storageObj, legacyObj runtime.Object) bool {
44+
//TODO: define the comparison logic between a playlist returned by the storage and a playlist returned by the legacy storage
45+
return false
46+
}

pkg/registry/apis/scope/storage.go

+6
Original file line numberDiff line numberDiff line change
@@ -184,3 +184,9 @@ func SelectableScopeNodeFields(obj *scope.ScopeNode) fields.Set {
184184
"spec.parentName": parentName,
185185
})
186186
}
187+
188+
// Compare asserts on the equality of objects returned from both stores (object storage and legacy storage)
189+
func (s *storage) Compare(storageObj, legacyObj runtime.Object) bool {
190+
//TODO: define the comparison logic between a scope returned by the storage and a scope returned by the legacy storage
191+
return false
192+
}

pkg/registry/apis/service/storage.go

+6
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,9 @@ func newStorage(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter) (*
6060
}
6161
return &storage{Store: store}, nil
6262
}
63+
64+
// Compare asserts on the equality of objects returned from both stores (object storage and legacy storage)
65+
func (s *storage) Compare(storageObj, legacyObj runtime.Object) bool {
66+
//TODO: define the comparison logic between a generic object returned by the storage and a generic object returned by the legacy storage
67+
return false
68+
}

0 commit comments

Comments
 (0)