mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Add and fix tests for playlists in mode1 (#88543)
* Add and fix tests for playlists in mode1 * Make etcd tests pass mode1 for now * Fix mode1 and add more tests for playlists in mode 1 * Remove repeated test * Fix test setup
This commit is contained in:
parent
f78fb4ff16
commit
b30c81b1ad
@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
@ -49,20 +50,23 @@ func (d *DualWriterMode1) Create(ctx context.Context, original runtime.Object, c
|
|||||||
}
|
}
|
||||||
d.recordLegacyDuration(false, mode1Str, options.Kind, method, startLegacy)
|
d.recordLegacyDuration(false, mode1Str, options.Kind, method, startLegacy)
|
||||||
|
|
||||||
|
createdCopy := created.DeepCopyObject()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
ctx, cancel := context.WithTimeoutCause(ctx, time.Second*10, errors.New("storage create timeout"))
|
ctx, cancel := context.WithTimeoutCause(ctx, time.Second*10, errors.New("storage create timeout"))
|
||||||
createdLegacy, err := enrichLegacyObject(original, created, true)
|
defer cancel()
|
||||||
if err != nil {
|
|
||||||
|
objStorage, errEnrichObj := enrichLegacyObject(original, createdCopy, true)
|
||||||
|
if errEnrichObj != nil {
|
||||||
cancel()
|
cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
startStorage := time.Now()
|
startStorage := time.Now()
|
||||||
defer cancel()
|
_, errObjectSt := d.Storage.Create(ctx, objStorage, createValidation, options)
|
||||||
_, errObjectSt := d.Storage.Create(ctx, createdLegacy, createValidation, options)
|
|
||||||
d.recordStorageDuration(errObjectSt != nil, mode1Str, options.Kind, method, startStorage)
|
d.recordStorageDuration(errObjectSt != nil, mode1Str, options.Kind, method, startStorage)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return created, nil
|
return created, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get overrides the behavior of the generic DualWriter and reads only from LegacyStorage.
|
// Get overrides the behavior of the generic DualWriter and reads only from LegacyStorage.
|
||||||
@ -135,7 +139,7 @@ func (d *DualWriterMode1) Delete(ctx context.Context, name string, deleteValidat
|
|||||||
d.recordStorageDuration(err != nil, mode1Str, options.Kind, method, startStorage)
|
d.recordStorageDuration(err != nil, mode1Str, options.Kind, method, startStorage)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return res, async, nil
|
return res, async, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteCollection overrides the behavior of the generic DualWriter and deletes only from LegacyStorage.
|
// DeleteCollection overrides the behavior of the generic DualWriter and deletes only from LegacyStorage.
|
||||||
@ -161,7 +165,7 @@ func (d *DualWriterMode1) DeleteCollection(ctx context.Context, deleteValidation
|
|||||||
d.recordStorageDuration(err != nil, mode1Str, options.Kind, method, startStorage)
|
d.recordStorageDuration(err != nil, mode1Str, options.Kind, method, startStorage)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return res, nil
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DualWriterMode1) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
|
func (d *DualWriterMode1) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
|
||||||
@ -180,27 +184,34 @@ func (d *DualWriterMode1) Update(ctx context.Context, name string, objInfo rest.
|
|||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
ctx, cancel := context.WithTimeoutCause(ctx, time.Second*10, errors.New("storage update timeout"))
|
ctx, cancel := context.WithTimeoutCause(ctx, time.Second*10, errors.New("storage update timeout"))
|
||||||
updated, err := objInfo.UpdatedObject(ctx, res)
|
|
||||||
if err != nil {
|
|
||||||
log.WithValues("object", updated).Error(err, "could not update or create object")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
resCopy := res.DeepCopyObject()
|
||||||
// get the object to be updated
|
// get the object to be updated
|
||||||
foundObj, err := d.Storage.Get(ctx, name, &metav1.GetOptions{})
|
foundObj, err := d.Storage.Get(ctx, name, &metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithValues("object", foundObj).Error(err, "could not get object to update")
|
if !apierrors.IsNotFound(err) {
|
||||||
|
log.WithValues("object", foundObj).Error(err, "could not get object to update")
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
log.Info("object not found for update, creating one")
|
||||||
|
}
|
||||||
|
|
||||||
|
updated, err := objInfo.UpdatedObject(ctx, resCopy)
|
||||||
|
if err != nil {
|
||||||
|
log.WithValues("object", updated).Error(err, "could not update or create object")
|
||||||
|
cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the object is found, create a new updateWrapper with the object found
|
// if the object is found, create a new updateWrapper with the object found
|
||||||
if foundObj != nil {
|
if foundObj != nil {
|
||||||
res, err := enrichLegacyObject(foundObj, res, false)
|
resCopy, err := enrichLegacyObject(foundObj, resCopy, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err, "could not enrich object")
|
log.Error(err, "could not enrich object")
|
||||||
cancel()
|
cancel()
|
||||||
}
|
}
|
||||||
objInfo = &updateWrapper{
|
objInfo = &updateWrapper{
|
||||||
upstream: objInfo,
|
upstream: objInfo,
|
||||||
updated: res,
|
updated: resCopy,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
startStorage := time.Now()
|
startStorage := time.Now()
|
||||||
@ -209,7 +220,7 @@ func (d *DualWriterMode1) Update(ctx context.Context, name string, objInfo rest.
|
|||||||
d.recordStorageDuration(errObjectSt != nil, mode1Str, options.Kind, method, startStorage)
|
d.recordStorageDuration(errObjectSt != nil, mode1Str, options.Kind, method, startStorage)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return res, async, nil
|
return res, async, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DualWriterMode1) Destroy() {
|
func (d *DualWriterMode1) Destroy() {
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
@ -14,6 +15,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var exampleObj = &example.Pod{TypeMeta: metav1.TypeMeta{Kind: "foo"}, ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "1"}, Spec: example.PodSpec{}, Status: example.PodStatus{}}
|
var exampleObj = &example.Pod{TypeMeta: metav1.TypeMeta{Kind: "foo"}, ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "1"}, Spec: example.PodSpec{}, Status: example.PodStatus{}}
|
||||||
|
var exampleObjNoRV = &example.Pod{TypeMeta: metav1.TypeMeta{Kind: "foo"}, ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: ""}, Spec: example.PodSpec{}, Status: example.PodStatus{}}
|
||||||
var exampleObjDifferentRV = &example.Pod{TypeMeta: metav1.TypeMeta{Kind: "foo"}, ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "3"}, Spec: example.PodSpec{}, Status: example.PodStatus{}}
|
var exampleObjDifferentRV = &example.Pod{TypeMeta: metav1.TypeMeta{Kind: "foo"}, ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "3"}, Spec: example.PodSpec{}, Status: example.PodStatus{}}
|
||||||
var anotherObj = &example.Pod{TypeMeta: metav1.TypeMeta{Kind: "foo"}, ObjectMeta: metav1.ObjectMeta{Name: "bar", ResourceVersion: "2"}, Spec: example.PodSpec{}, Status: example.PodStatus{}}
|
var anotherObj = &example.Pod{TypeMeta: metav1.TypeMeta{Kind: "foo"}, ObjectMeta: metav1.ObjectMeta{Name: "bar", ResourceVersion: "2"}, Spec: example.PodSpec{}, Status: example.PodStatus{}}
|
||||||
var failingObj = &example.Pod{TypeMeta: metav1.TypeMeta{Kind: "foo"}, ObjectMeta: metav1.ObjectMeta{Name: "object-fail", ResourceVersion: "2"}, Spec: example.PodSpec{}, Status: example.PodStatus{}}
|
var failingObj = &example.Pod{TypeMeta: metav1.TypeMeta{Kind: "foo"}, ObjectMeta: metav1.ObjectMeta{Name: "object-fail", ResourceVersion: "2"}, Spec: example.PodSpec{}, Status: example.PodStatus{}}
|
||||||
@ -24,7 +26,7 @@ func TestMode1_Create(t *testing.T) {
|
|||||||
type testCase struct {
|
type testCase struct {
|
||||||
input runtime.Object
|
input runtime.Object
|
||||||
setupLegacyFn func(m *mock.Mock, input runtime.Object)
|
setupLegacyFn func(m *mock.Mock, input runtime.Object)
|
||||||
setupStorageFn func(m *mock.Mock, input runtime.Object)
|
setupStorageFn func(m *mock.Mock)
|
||||||
name string
|
name string
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}
|
}
|
||||||
@ -36,8 +38,8 @@ func TestMode1_Create(t *testing.T) {
|
|||||||
setupLegacyFn: func(m *mock.Mock, input runtime.Object) {
|
setupLegacyFn: func(m *mock.Mock, input runtime.Object) {
|
||||||
m.On("Create", mock.Anything, input, mock.Anything, mock.Anything).Return(exampleObj, nil)
|
m.On("Create", mock.Anything, input, mock.Anything, mock.Anything).Return(exampleObj, nil)
|
||||||
},
|
},
|
||||||
setupStorageFn: func(m *mock.Mock, input runtime.Object) {
|
setupStorageFn: func(m *mock.Mock) {
|
||||||
m.On("Create", mock.Anything, anotherObj, mock.Anything, mock.Anything).Return(anotherObj, nil)
|
m.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(exampleObjNoRV, nil)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -63,7 +65,7 @@ func TestMode1_Create(t *testing.T) {
|
|||||||
tt.setupLegacyFn(m, tt.input)
|
tt.setupLegacyFn(m, tt.input)
|
||||||
}
|
}
|
||||||
if tt.setupStorageFn != nil {
|
if tt.setupStorageFn != nil {
|
||||||
tt.setupStorageFn(m, tt.input)
|
tt.setupStorageFn(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
dw := NewDualWriter(Mode1, ls, us)
|
dw := NewDualWriter(Mode1, ls, us)
|
||||||
@ -75,9 +77,9 @@ func TestMode1_Create(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
us.AssertNotCalled(t, "Create", context.Background(), tt.input, func(context.Context, runtime.Object) error { return nil }, &metav1.CreateOptions{})
|
acc, err := meta.Accessor(obj)
|
||||||
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, obj, exampleObj)
|
assert.Equal(t, acc.GetResourceVersion(), "1")
|
||||||
assert.NotEqual(t, obj, anotherObj)
|
assert.NotEqual(t, obj, anotherObj)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -101,21 +101,19 @@ func TestIntegrationPlaylist(t *testing.T) {
|
|||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
// #TODO Enable this test once we have fixed dual writing mode 1 behavior
|
t.Run("with dual write (file, mode 1)", func(t *testing.T) {
|
||||||
// Do_CRUD_via_k8s_(and_check_that_legacy_api_still_works) breaks
|
doPlaylistTests(t, apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||||
// t.Run("with dual write (file, mode 1)", func(t *testing.T) {
|
AppModeProduction: true,
|
||||||
// doPlaylistTests(t, apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
DisableAnonymous: true,
|
||||||
// AppModeProduction: true,
|
APIServerStorageType: "file", // write the files to disk
|
||||||
// DisableAnonymous: true,
|
EnableFeatureToggles: []string{
|
||||||
// APIServerStorageType: "file", // write the files to disk
|
featuremgmt.FlagKubernetesPlaylists, // Required so that legacy calls are also written
|
||||||
// EnableFeatureToggles: []string{
|
},
|
||||||
// featuremgmt.FlagKubernetesPlaylists, // Required so that legacy calls are also written
|
DualWriterDesiredModes: map[string]grafanarest.DualWriterMode{
|
||||||
// },
|
playlistv0alpha1.GROUPRESOURCE: grafanarest.Mode1,
|
||||||
// DualWriterDesiredModes: map[string]grafanarest.DualWriterMode{
|
},
|
||||||
// playlistv0alpha1.GROUPRESOURCE: grafanarest.Mode1,
|
}))
|
||||||
// },
|
})
|
||||||
// }))
|
|
||||||
// })
|
|
||||||
|
|
||||||
t.Run("with dual write (file, mode 2)", func(t *testing.T) {
|
t.Run("with dual write (file, mode 2)", func(t *testing.T) {
|
||||||
doPlaylistTests(t, apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
doPlaylistTests(t, apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||||
@ -146,22 +144,20 @@ func TestIntegrationPlaylist(t *testing.T) {
|
|||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
// #TODO Enable this test once we have fixed dual writing mode 1 behavior
|
t.Run("with dual write (unified storage, mode 1)", func(t *testing.T) {
|
||||||
// Do_CRUD_via_k8s_(and_check_that_legacy_api_still_works) breaks
|
doPlaylistTests(t, apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||||
// t.Run("with dual write (unified storage, mode 1)", func(t *testing.T) {
|
AppModeProduction: false, // required for unified storage
|
||||||
// doPlaylistTests(t, apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
DisableAnonymous: true,
|
||||||
// AppModeProduction: false, // required for unified storage
|
APIServerStorageType: "unified", // use the entity api tables
|
||||||
// DisableAnonymous: true,
|
EnableFeatureToggles: []string{
|
||||||
// APIServerStorageType: "unified", // use the entity api tables
|
featuremgmt.FlagUnifiedStorage,
|
||||||
// EnableFeatureToggles: []string{
|
featuremgmt.FlagKubernetesPlaylists, // Required so that legacy calls are also written
|
||||||
// featuremgmt.FlagUnifiedStorage,
|
},
|
||||||
// featuremgmt.FlagKubernetesPlaylists, // Required so that legacy calls are also written
|
DualWriterDesiredModes: map[string]grafanarest.DualWriterMode{
|
||||||
// },
|
playlistv0alpha1.GROUPRESOURCE: grafanarest.Mode1,
|
||||||
// DualWriterDesiredModes: map[string]grafanarest.DualWriterMode{
|
},
|
||||||
// playlistv0alpha1.GROUPRESOURCE: grafanarest.Mode1,
|
}))
|
||||||
// },
|
})
|
||||||
// }))
|
|
||||||
// })
|
|
||||||
|
|
||||||
t.Run("with dual write (unified storage, mode 2)", func(t *testing.T) {
|
t.Run("with dual write (unified storage, mode 2)", func(t *testing.T) {
|
||||||
doPlaylistTests(t, apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
doPlaylistTests(t, apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||||
|
Loading…
Reference in New Issue
Block a user