Unistore: Reuse MySQL and Postgres Grafana core config instead of the object (#94223)

* Reuse MySQL and Postgres Grafana config instead of the object

- Only reuse the Grafana DB object for SQLite. Support for SQLite will be added in a different PR
- Fail when reusing the Grafana DB object if it is using DB instrumentation
- In the case that we have to reuse a Grafana DB with its instrumentation, fail with an error that describes a workaround
- Add regression tests to reproduce incident 2144

* remove temp file

* fix linter

* fix linter x2

* fix linter x3
This commit is contained in:
Diego Augusto Molina
2024-10-04 09:07:20 -03:00
committed by GitHub
parent a82f102878
commit 7d32d5eff4
9 changed files with 562 additions and 81 deletions

View File

@@ -0,0 +1,159 @@
package dbimpl
import (
"context"
"database/sql"
"net/http"
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/trace"
traceNoop "go.opentelemetry.io/otel/trace/noop"
ini "gopkg.in/ini.v1"
"github.com/grafana/grafana/pkg/bus"
infraDB "github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
)
type (
// cfgSectionMap represents an INI section, mapping from an INI key to an
// INI value.
cfgSectionMap = map[string]string
// cfgMap is a map from INI section name to INI section contents.
cfgMap = map[string]cfgSectionMap
)
// setupDBForGrafana modifies `m` in the following way:
//
// [database]
// type = sqlite3
// path = unique-random-path
//
// After that, it initializes a temporary SQLite filesystem-backed database that
// is later deleted when the test finishes.
func setupDBForGrafana(t *testing.T, ctx context.Context, m cfgMap) {
dbSection, ok := m["database"]
if !ok {
dbSection = cfgSectionMap{}
m["database"] = dbSection
}
dbSection["type"] = "sqlite3"
dbSection["path"] = t.TempDir() + "/" + uuid.New().String()
db, err := sql.Open("sqlite3", "file:"+dbSection["path"])
require.NoError(t, err)
_, err = db.ExecContext(ctx, `
CREATE TABLE user (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
version INTEGER NOT NULL,
login TEXT NOT NULL,
email TEXT NOT NULL,
name TEXT NULL,
password TEXT NULL,
salt TEXT NULL,
rands TEXT NULL,
company TEXT NULL,
org_id INTEGER NOT NULL,
is_admin INTEGER NOT NULL,
email_verified INTEGER NULL,
theme TEXT NULL,
created DATETIME NOT NULL,
updated DATETIME NOT NULL,
help_flags1 INTEGER NOT NULL DEFAULT 0,
last_seen_at DATETIME NULL,
is_disabled INTEGER NOT NULL DEFAULT 0,
is_service_account BOOLEAN DEFAULT 0,
uid TEXT NULL
);
CREATE TABLE org (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
version INTEGER NOT NULL,
name TEXT NOT NULL,
address1 TEXT NULL,
address2 TEXT NULL,
city TEXT NULL,
state TEXT NULL,
zip_code TEXT NULL,
country TEXT NULL,
billing_email TEXT NULL,
created DATETIME NOT NULL,
updated DATETIME NOT NULL
);
CREATE TABLE org_user (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
org_id INTEGER NOT NULL,
user_id INTEGER NOT NULL,
role TEXT NOT NULL,
created DATETIME NOT NULL,
updated DATETIME NOT NULL
);
`)
require.NoError(t, err)
}
func newTestInfraDB(t *testing.T, m cfgMap) infraDB.DB {
t.Helper()
// nil migrations means no migrations
sqlstoreDB, err := sqlstore.ProvideService(
newCfgFromIniMap(t, m), // *setting.Cfg
featureTogglesNop{}, // featuremgmt.FeatureToggles
nil, // registry.DatabaseMigrator
nopBus{}, // github.com/grafana/grafana/pkg/bus.Bus
newNopTestGrafanaTracer(),
)
require.NoError(t, err)
return sqlstoreDB
}
func newCfgFromIniMap(t *testing.T, m cfgMap) *setting.Cfg {
t.Helper()
cfg, err := setting.NewCfgFromINIFile(newTestINIFile(t, m))
require.NoError(t, err)
return cfg
}
func newTestINIFile(t *testing.T, m cfgMap) *ini.File {
t.Helper()
f := ini.Empty()
for sectionName, kvs := range m {
section, err := f.NewSection(sectionName)
require.NoError(t, err)
for k, v := range kvs {
_, err := section.NewKey(k, v)
require.NoError(t, err)
}
}
return f
}
type (
testGrafanaTracer struct {
trace.Tracer
}
featureTogglesNop struct{}
nopBus struct{}
)
func (testGrafanaTracer) Inject(context.Context, http.Header, trace.Span) {}
func newNopTestGrafanaTracer() tracing.Tracer {
return testGrafanaTracer{traceNoop.NewTracerProvider().Tracer("test")}
}
func (featureTogglesNop) IsEnabled(context.Context, string) bool {
return false
}
func (featureTogglesNop) IsEnabledGlobally(string) bool {
return false
}
func (featureTogglesNop) GetEnabled(context.Context) map[string]bool {
return map[string]bool{}
}
func (nopBus) Publish(context.Context, bus.Msg) error { return nil }
func (nopBus) AddEventListener(bus.HandlerFunc) {}