DualWriter: Error when folder+dashboard modes do not match when running mode4+ (#111286)
This commit is contained in:
@@ -507,7 +507,10 @@ func Initialize(ctx context.Context, cfg *setting.Cfg, opts Options, apiOpts api
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dualwriteService := dualwrite.ProvideService(featureToggles, registerer, kvStore, cfg)
|
||||
dualwriteService, err := dualwrite.ProvideService(featureToggles, kvStore, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sortService := sort.ProvideService()
|
||||
folderimplService := folderimpl.ProvideService(folderStoreImpl, accessControl, inProcBus, dashboardsStore, userService, sqlStore, featureToggles, bundleregistryService, publicDashboardServiceWrapperImpl, cfg, registerer, tracer, resourceClient, dualwriteService, sortService, eventualRestConfigProvider)
|
||||
searchService := searchV2.ProvideService(cfg, sqlStore, entityEventsService, acimplService, tracingService, featureToggles, orgService, userService, folderimplService)
|
||||
@@ -1102,7 +1105,10 @@ func InitializeForTest(ctx context.Context, t sqlutil.ITestDB, testingT interfac
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dualwriteService := dualwrite.ProvideService(featureToggles, registerer, kvStore, cfg)
|
||||
dualwriteService, err := dualwrite.ProvideService(featureToggles, kvStore, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sortService := sort.ProvideService()
|
||||
folderimplService := folderimpl.ProvideService(folderStoreImpl, accessControl, inProcBus, dashboardsStore, userService, sqlStore, featureToggles, bundleregistryService, publicDashboardServiceWrapperImpl, cfg, registerer, tracer, resourceClient, dualwriteService, sortService, eventualRestConfigProvider)
|
||||
searchService := searchV2.ProvideService(cfg, sqlStore, entityEventsService, acimplService, tracingService, featureToggles, orgService, userService, folderimplService)
|
||||
|
||||
@@ -77,7 +77,8 @@ func TestRuntime_Create(t *testing.T) {
|
||||
tt.setupStorageFn(us.Mock, tt.input)
|
||||
}
|
||||
|
||||
m := ProvideService(featuremgmt.WithFeatures(featuremgmt.FlagManagedDualWriter), nil, kvstore.NewFakeKVStore(), nil)
|
||||
m, err := ProvideService(featuremgmt.WithFeatures(featuremgmt.FlagManagedDualWriter), kvstore.NewFakeKVStore(), nil)
|
||||
require.NoError(t, err)
|
||||
dw, err := m.NewStorage(kind, ls, us)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -149,7 +150,8 @@ func TestRuntime_Get(t *testing.T) {
|
||||
tt.setupStorageFn(us.Mock, name)
|
||||
}
|
||||
|
||||
m := ProvideService(featuremgmt.WithFeatures(featuremgmt.FlagManagedDualWriter), nil, kvstore.NewFakeKVStore(), nil)
|
||||
m, err := ProvideService(featuremgmt.WithFeatures(featuremgmt.FlagManagedDualWriter), kvstore.NewFakeKVStore(), nil)
|
||||
require.NoError(t, err)
|
||||
dw, err := m.NewStorage(kind, ls, us)
|
||||
require.NoError(t, err)
|
||||
status, err := m.Status(context.Background(), kind)
|
||||
@@ -233,7 +235,8 @@ func TestRuntime_CreateWhileMigrating(t *testing.T) {
|
||||
}
|
||||
|
||||
// Shared provider across all tests
|
||||
dual := ProvideService(featuremgmt.WithFeatures(featuremgmt.FlagManagedDualWriter), nil, kvstore.NewFakeKVStore(), nil)
|
||||
dual, err := ProvideService(featuremgmt.WithFeatures(featuremgmt.FlagManagedDualWriter), kvstore.NewFakeKVStore(), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
@@ -24,39 +23,39 @@ func ProvideStaticServiceForTests(cfg *setting.Cfg) Service {
|
||||
|
||||
func ProvideService(
|
||||
features featuremgmt.FeatureToggles,
|
||||
reg prometheus.Registerer,
|
||||
kv kvstore.KVStore,
|
||||
cfg *setting.Cfg,
|
||||
) Service {
|
||||
) (Service, error) {
|
||||
enabled := features.IsEnabledGlobally(featuremgmt.FlagManagedDualWriter) ||
|
||||
features.IsEnabledGlobally(featuremgmt.FlagProvisioning) // required for git provisioning
|
||||
|
||||
if cfg != nil {
|
||||
// Avoid dynamic behavior when things are explicitly configured to mode5
|
||||
allMode5 := true
|
||||
for _, gr := range []string{
|
||||
"dashboards.dashboard.grafana.app",
|
||||
"folders.folder.grafana.app",
|
||||
} {
|
||||
if cfg.UnifiedStorage[gr].DualWriterMode != rest.Mode5 {
|
||||
allMode5 = false
|
||||
break
|
||||
if !enabled {
|
||||
return &staticService{cfg}, nil
|
||||
}
|
||||
|
||||
if cfg != nil {
|
||||
foldersMode := cfg.UnifiedStorage["folders.folder.grafana.app"].DualWriterMode
|
||||
dashboardsMode := cfg.UnifiedStorage["dashboards.dashboard.grafana.app"].DualWriterMode
|
||||
|
||||
// If both are fully on unified (Mode5), the dynamic service is not needed.
|
||||
if foldersMode == rest.Mode5 && dashboardsMode == rest.Mode5 {
|
||||
return &staticService{cfg}, nil
|
||||
}
|
||||
|
||||
if (foldersMode >= rest.Mode4 || dashboardsMode >= rest.Mode4) && foldersMode != dashboardsMode {
|
||||
return nil, fmt.Errorf("dashboards and folders must use the same mode when reading from unified storage")
|
||||
}
|
||||
}
|
||||
if allMode5 || !enabled {
|
||||
return &staticService{cfg} // fallback to using the dual write flags from cfg
|
||||
}
|
||||
}
|
||||
|
||||
db := &keyvalueDB{
|
||||
db: kv,
|
||||
logger: logging.DefaultLogger.With("logger", "dualwrite.kv"),
|
||||
}
|
||||
|
||||
return &service{
|
||||
db: db,
|
||||
db: &keyvalueDB{
|
||||
db: kv,
|
||||
logger: logging.DefaultLogger.With("logger", "dualwrite.kv"),
|
||||
},
|
||||
enabled: enabled,
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
type service struct {
|
||||
|
||||
@@ -17,7 +17,8 @@ import (
|
||||
func TestService(t *testing.T) {
|
||||
t.Run("dynamic", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
mode := ProvideService(featuremgmt.WithFeatures(), nil, kvstore.NewFakeKVStore(), nil)
|
||||
mode, err := ProvideService(featuremgmt.WithFeatures(), kvstore.NewFakeKVStore(), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
gr := schema.GroupResource{Group: "ggg", Resource: "rrr"}
|
||||
status, err := mode.Status(ctx, gr)
|
||||
@@ -69,6 +70,7 @@ func TestService(t *testing.T) {
|
||||
isStatic bool
|
||||
foldersFromUnified bool
|
||||
dashboardsFromUnified bool
|
||||
error string
|
||||
}
|
||||
|
||||
for _, tc := range []testCase{{
|
||||
@@ -93,14 +95,40 @@ func TestService(t *testing.T) {
|
||||
cfg: setting.Cfg{
|
||||
UnifiedStorage: map[string]setting.UnifiedStorageConfig{
|
||||
"dashboards.dashboard.grafana.app": {
|
||||
DualWriterMode: rest.Mode5,
|
||||
DualWriterMode: rest.Mode3,
|
||||
},
|
||||
},
|
||||
}}, {
|
||||
name: "invalid folder mode4",
|
||||
flags: featuremgmt.WithFeatures(featuremgmt.FlagProvisioning),
|
||||
error: "must use the same mode",
|
||||
cfg: setting.Cfg{
|
||||
UnifiedStorage: map[string]setting.UnifiedStorageConfig{
|
||||
"folders.folder.grafana.app": {
|
||||
DualWriterMode: rest.Mode4,
|
||||
},
|
||||
},
|
||||
}}, {
|
||||
name: "invalid dashboards mode4",
|
||||
flags: featuremgmt.WithFeatures(featuremgmt.FlagProvisioning),
|
||||
error: "must use the same mode",
|
||||
cfg: setting.Cfg{
|
||||
UnifiedStorage: map[string]setting.UnifiedStorageConfig{
|
||||
"dashboards.dashboard.grafana.app": {
|
||||
DualWriterMode: rest.Mode4,
|
||||
},
|
||||
},
|
||||
}},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
svc := ProvideService(tc.flags, nil, kvstore.NewFakeKVStore(), &tc.cfg)
|
||||
svc, err := ProvideService(tc.flags, kvstore.NewFakeKVStore(), &tc.cfg)
|
||||
if tc.error != "" {
|
||||
require.ErrorContains(t, err, tc.error)
|
||||
require.Nil(t, svc, "expect a nil service when an error exts")
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
_, isStatic := svc.(*staticService)
|
||||
require.Equal(t, tc.isStatic, isStatic)
|
||||
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
dashboardV0 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
|
||||
foldersV1 "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1"
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
@@ -43,7 +42,7 @@ func TestIntegrationFolderTree(t *testing.T) {
|
||||
}
|
||||
|
||||
modes := []grafanarest.DualWriterMode{
|
||||
// grafanarest.Mode1, (nothing new tested)
|
||||
// grafanarest.Mode1, (nothing new tested in mode 0 or 1)
|
||||
grafanarest.Mode2, // write both, read legacy
|
||||
grafanarest.Mode3, // write both, read unified
|
||||
grafanarest.Mode4,
|
||||
@@ -51,7 +50,7 @@ func TestIntegrationFolderTree(t *testing.T) {
|
||||
}
|
||||
for _, mode := range modes {
|
||||
t.Run(fmt.Sprintf("mode %d", mode), func(t *testing.T) {
|
||||
flags := []string{featuremgmt.FlagManagedDualWriter}
|
||||
flags := []string{}
|
||||
if mode >= grafanarest.Mode3 { // make sure modes 0-3 work without it
|
||||
flags = append(flags, featuremgmt.FlagUnifiedStorageSearch)
|
||||
}
|
||||
@@ -61,12 +60,13 @@ func TestIntegrationFolderTree(t *testing.T) {
|
||||
APIServerStorageType: "unified",
|
||||
EnableFeatureToggles: flags,
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
foldersV1.RESOURCEGROUP: {
|
||||
"dashboards.dashboard.grafana.app": {
|
||||
DualWriterMode: mode,
|
||||
},
|
||||
"folders.folder.grafana.app": {
|
||||
DualWriterMode: mode,
|
||||
},
|
||||
},
|
||||
// We set it to 1 here, so we always get forced pagination based on the response size.
|
||||
UnifiedStorageMaxPageSizeBytes: 1,
|
||||
})
|
||||
defer helper.Shutdown()
|
||||
|
||||
@@ -209,6 +209,13 @@ func (f *FolderDefinition) CreateWithLegacyAPI(t *testing.T, h *apis.K8sTestHelp
|
||||
require.NoError(t, result.Error(), f.Name)
|
||||
require.Equal(t, int(http.StatusOK), statusCode, f.Name)
|
||||
}
|
||||
|
||||
// Now check that we could get the folder
|
||||
result = client.Get().AbsPath("api", "folders", f.Name).
|
||||
Do(context.Background()).
|
||||
StatusCode(&statusCode)
|
||||
require.NoErrorf(t, result.Error(), "get folder after create: %s", f.Name)
|
||||
require.Equal(t, int(http.StatusOK), statusCode, f.Name)
|
||||
}
|
||||
|
||||
for _, child := range f.Children {
|
||||
|
||||
Reference in New Issue
Block a user