Skip to content

Commit 2645958

Browse files
Use global function to compare any entity to both stores (grafana#89282)
* WIP implement generic compare interface * Use global compare fn for all entities * Lint * Update pkg/apiserver/rest/dualwriter.go Co-authored-by: Dan Cech <[email protected]> * Don't need to hash, just compare bytes * Fix tests --------- Co-authored-by: Dan Cech <[email protected]>
1 parent 4651506 commit 2645958

File tree

13 files changed

+52
-66
lines changed

13 files changed

+52
-66
lines changed

pkg/apiserver/rest/dualwriter.go

+24-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package rest
22

33
import (
4+
"bytes"
45
"context"
6+
"encoding/json"
57
"errors"
68
"fmt"
79

@@ -35,8 +37,6 @@ type Storage interface {
3537
rest.CreaterUpdater
3638
rest.GracefulDeleter
3739
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
4040
}
4141

4242
// LegacyStorage is a storage implementation that writes to the Grafana SQL database.
@@ -207,3 +207,25 @@ func SetDualWritingMode(
207207

208208
return NewDualWriter(currentMode, legacy, storage, reg), nil
209209
}
210+
211+
var defaultConverter = runtime.UnstructuredConverter(runtime.DefaultUnstructuredConverter)
212+
213+
// Compare asserts on the equality of objects returned from both stores (object storage and legacy storage)
214+
func Compare(storageObj, legacyObj runtime.Object) bool {
215+
return bytes.Equal(removeMeta(storageObj), removeMeta(legacyObj))
216+
}
217+
218+
func removeMeta(obj runtime.Object) []byte {
219+
cpy := obj.DeepCopyObject()
220+
unstObj, err := defaultConverter.ToUnstructured(cpy)
221+
if err != nil {
222+
return nil
223+
}
224+
// we don't want to compare meta fields
225+
delete(unstObj, "meta")
226+
jsonObj, err := json.Marshal(cpy)
227+
if err != nil {
228+
return nil
229+
}
230+
return jsonObj
231+
}

pkg/apiserver/rest/dualwriter_mode1.go

-4
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,3 @@ func (d *DualWriterMode1) NewList() runtime.Object {
243243
func (d *DualWriterMode1) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
244244
return d.Legacy.ConvertToTable(ctx, object, tableOptions)
245245
}
246-
247-
func (d *DualWriterMode1) Compare(storageObj, legacyObj runtime.Object) bool {
248-
return d.Storage.Compare(storageObj, legacyObj)
249-
}

pkg/apiserver/rest/dualwriter_mode1_test.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"errors"
66
"testing"
7+
"time"
78

89
"github.com/prometheus/client_golang/prometheus"
910
"github.com/stretchr/testify/assert"
@@ -15,10 +16,10 @@ import (
1516
"k8s.io/apiserver/pkg/apis/example"
1617
)
1718

18-
var exampleObj = &example.Pod{TypeMeta: metav1.TypeMeta{Kind: "foo"}, ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "1"}, Spec: example.PodSpec{}, Status: example.PodStatus{}}
19-
var exampleObjNoRV = &example.Pod{TypeMeta: metav1.TypeMeta{Kind: "foo"}, ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: ""}, Spec: example.PodSpec{}, Status: example.PodStatus{}}
19+
var exampleObj = &example.Pod{TypeMeta: metav1.TypeMeta{Kind: "foo"}, ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "1", CreationTimestamp: metav1.Time{}}, Spec: example.PodSpec{}, Status: example.PodStatus{StartTime: &metav1.Time{Time: time.Now()}}}
20+
var exampleObjNoRV = &example.Pod{TypeMeta: metav1.TypeMeta{Kind: "foo"}, ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "", CreationTimestamp: metav1.Time{}}, Spec: example.PodSpec{}, Status: example.PodStatus{StartTime: &metav1.Time{Time: time.Now()}}}
2021
var exampleObjDifferentRV = &example.Pod{TypeMeta: metav1.TypeMeta{Kind: "foo"}, ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "3"}, Spec: example.PodSpec{}, Status: example.PodStatus{}}
21-
var anotherObj = &example.Pod{TypeMeta: metav1.TypeMeta{Kind: "foo"}, ObjectMeta: metav1.ObjectMeta{Name: "bar", ResourceVersion: "2"}, Spec: example.PodSpec{}, Status: example.PodStatus{}}
22+
var anotherObj = &example.Pod{TypeMeta: metav1.TypeMeta{Kind: "foo"}, ObjectMeta: metav1.ObjectMeta{Name: "bar", ResourceVersion: "2"}, Spec: example.PodSpec{}, Status: example.PodStatus{StartTime: &metav1.Time{Time: time.Now()}}}
2223
var failingObj = &example.Pod{TypeMeta: metav1.TypeMeta{Kind: "foo"}, ObjectMeta: metav1.ObjectMeta{Name: "object-fail", ResourceVersion: "2"}, Spec: example.PodSpec{}, Status: example.PodStatus{}}
2324
var exampleList = &example.PodList{TypeMeta: metav1.TypeMeta{Kind: "foo"}, ListMeta: metav1.ListMeta{}, Items: []example.Pod{*exampleObj}}
2425
var anotherList = &example.PodList{Items: []example.Pod{*anotherObj}}

pkg/apiserver/rest/dualwriter_mode2.go

-4
Original file line numberDiff line numberDiff line change
@@ -306,10 +306,6 @@ func (d *DualWriterMode2) ConvertToTable(ctx context.Context, object runtime.Obj
306306
return d.Storage.ConvertToTable(ctx, object, tableOptions)
307307
}
308308

309-
func (d *DualWriterMode2) Compare(storageObj, legacyObj runtime.Object) bool {
310-
return d.Storage.Compare(storageObj, legacyObj)
311-
}
312-
313309
func parseList(legacyList []runtime.Object) (metainternalversion.ListOptions, map[string]int, error) {
314310
options := metainternalversion.ListOptions{}
315311
originKeys := []string{}

pkg/apiserver/rest/dualwriter_mode3.go

-4
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,3 @@ func (d *DualWriterMode3) NewList() runtime.Object {
155155
func (d *DualWriterMode3) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
156156
return d.Storage.ConvertToTable(ctx, object, tableOptions)
157157
}
158-
159-
func (d *DualWriterMode3) Compare(storageObj, legacyObj runtime.Object) bool {
160-
return d.Storage.Compare(storageObj, legacyObj)
161-
}

pkg/apiserver/rest/dualwriter_mode4.go

-4
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,3 @@ func (d *DualWriterMode4) NewList() runtime.Object {
8383
func (d *DualWriterMode4) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
8484
return d.Storage.ConvertToTable(ctx, object, tableOptions)
8585
}
86-
87-
func (d *DualWriterMode4) Compare(storageObj, legacyObj runtime.Object) bool {
88-
return d.Storage.Compare(storageObj, legacyObj)
89-
}

pkg/apiserver/rest/dualwriter_test.go

+24
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/prometheus/client_golang/prometheus"
1111
"github.com/stretchr/testify/assert"
1212
"github.com/stretchr/testify/mock"
13+
"k8s.io/apimachinery/pkg/runtime"
1314
)
1415

1516
func TestSetDualWritingMode(t *testing.T) {
@@ -58,3 +59,26 @@ func TestSetDualWritingMode(t *testing.T) {
5859
assert.Equal(t, val, fmt.Sprint(tt.expectedMode))
5960
}
6061
}
62+
63+
func TestCompare(t *testing.T) {
64+
testCase := []struct {
65+
name string
66+
input runtime.Object
67+
expected bool
68+
}{
69+
{
70+
name: "should return true when both objects are the same",
71+
input: exampleObj,
72+
expected: true,
73+
},
74+
{
75+
name: "should return false when objects are different",
76+
input: anotherObj,
77+
},
78+
}
79+
for _, tt := range testCase {
80+
t.Run(tt.name, func(t *testing.T) {
81+
assert.Equal(t, tt.expected, Compare(tt.input, exampleObj))
82+
})
83+
}
84+
}

pkg/registry/apis/dashboard/storage.go

-6
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,3 @@ func newStorage(scheme *runtime.Scheme) (*storage, error) {
6666
})
6767
return &storage{Store: store}, nil
6868
}
69-
70-
// Compare asserts on the equality of objects returned from both stores (object storage and legacy storage)
71-
func (s *storage) Compare(storageObj, legacyObj runtime.Object) bool {
72-
//TODO: define the comparison logic between a dashboard returned by the storage and a dashboard returned by the legacy storage
73-
return false
74-
}

pkg/registry/apis/folders/storage.go

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

pkg/registry/apis/peakq/storage.go

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

pkg/registry/apis/playlist/storage.go

-15
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package playlist
22

33
import (
4-
"k8s.io/apimachinery/pkg/api/meta"
54
"k8s.io/apimachinery/pkg/runtime"
65
"k8s.io/apiserver/pkg/registry/generic"
76
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
@@ -41,17 +40,3 @@ func newStorage(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter, le
4140
}
4241
return &storage{Store: store}, nil
4342
}
44-
45-
// Compare asserts on the equality of objects returned from both stores (object storage and legacy storage)
46-
func (s *storage) Compare(storageObj, legacyObj runtime.Object) bool {
47-
accStr, err := meta.Accessor(storageObj)
48-
if err != nil {
49-
return false
50-
}
51-
accLegacy, err := meta.Accessor(legacyObj)
52-
if err != nil {
53-
return false
54-
}
55-
56-
return accStr.GetName() == accLegacy.GetName()
57-
}

pkg/registry/apis/scope/storage.go

-6
Original file line numberDiff line numberDiff line change
@@ -207,9 +207,3 @@ func SelectableScopeNodeFields(obj *scope.ScopeNode) fields.Set {
207207
"spec.parentName": parentName,
208208
})
209209
}
210-
211-
// Compare asserts on the equality of objects returned from both stores (object storage and legacy storage)
212-
func (s *storage) Compare(storageObj, legacyObj runtime.Object) bool {
213-
//TODO: define the comparison logic between a scope returned by the storage and a scope returned by the legacy storage
214-
return false
215-
}

pkg/registry/apis/service/storage.go

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

0 commit comments

Comments
 (0)