[release-12.0.3] Unified storage: Respect GF_DATABASE_URL override (#107566)
Unified storage: Respect GF_DATABASE_URL override (#105331)
* Database for unified storage resources now reuses DB code that respects URL override.
Access instrument_queries via section getter.
(cherry picked from commit 041c343a86)
This commit is contained in:
@@ -12,7 +12,6 @@ import (
|
||||
"xorm.io/xorm"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/db"
|
||||
)
|
||||
|
||||
@@ -20,27 +19,16 @@ import (
|
||||
// driver.
|
||||
const tlsConfigName = "db_engine_tls"
|
||||
|
||||
func getEngine(cfg *setting.Cfg) (*xorm.Engine, error) {
|
||||
dbSection := cfg.SectionWithEnvOverrides("database")
|
||||
dbType := dbSection.Key("type").String()
|
||||
if dbType == "" {
|
||||
return nil, fmt.Errorf("no database type specified")
|
||||
}
|
||||
|
||||
switch dbType {
|
||||
func getEngine(config *sqlstore.DatabaseConfig) (*xorm.Engine, error) {
|
||||
switch config.Type {
|
||||
case dbTypeMySQL, dbTypePostgres, dbTypeSQLite:
|
||||
config, err := sqlstore.NewDatabaseConfig(cfg, nil)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
engine, err := xorm.NewEngine(dbType, config.ConnectionString)
|
||||
engine, err := xorm.NewEngine(config.Type, config.ConnectionString)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("open database: %w", err)
|
||||
}
|
||||
return engine, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported database type: %s", dbType)
|
||||
return nil, fmt.Errorf("unsupported database type: %s", config.Type)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,11 +9,14 @@ import (
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
func newValidMySQLGetter(withKeyPrefix bool) confGetter {
|
||||
@@ -30,7 +33,7 @@ func newValidMySQLGetter(withKeyPrefix bool) confGetter {
|
||||
}, prefix)
|
||||
}
|
||||
|
||||
func TestGetEngine(t *testing.T) {
|
||||
func TestNewResourceDbProvider(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("MySQL engine", func(t *testing.T) {
|
||||
@@ -43,9 +46,10 @@ func TestGetEngine(t *testing.T) {
|
||||
dbSection.Key("user").SetValue("user")
|
||||
dbSection.Key("password").SetValue("password")
|
||||
|
||||
engine, err := getEngine(cfg)
|
||||
engine, err := newResourceDBProvider(nil, cfg, nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, engine)
|
||||
require.Equal(t, dbTypeMySQL, engine.engine.Dialect().DriverName())
|
||||
})
|
||||
|
||||
t.Run("Postgres engine", func(t *testing.T) {
|
||||
@@ -58,9 +62,10 @@ func TestGetEngine(t *testing.T) {
|
||||
dbSection.Key("user").SetValue("user")
|
||||
dbSection.Key("password").SetValue("password")
|
||||
|
||||
engine, err := getEngine(cfg)
|
||||
engine, err := newResourceDBProvider(nil, cfg, nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, engine)
|
||||
require.Equal(t, dbTypePostgres, engine.engine.Dialect().DriverName())
|
||||
})
|
||||
|
||||
t.Run("SQLite engine", func(t *testing.T) {
|
||||
@@ -70,9 +75,20 @@ func TestGetEngine(t *testing.T) {
|
||||
dbSection.Key("type").SetValue(dbTypeSQLite)
|
||||
dbSection.Key("path").SetValue(":memory:")
|
||||
|
||||
engine, err := getEngine(cfg)
|
||||
engine, err := newResourceDBProvider(nil, cfg, nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, engine)
|
||||
require.Equal(t, dbTypeSQLite, engine.engine.Dialect().DriverName())
|
||||
})
|
||||
|
||||
t.Run("No database type", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
cfg := setting.NewCfg()
|
||||
|
||||
engine, err := newResourceDBProvider(nil, cfg, nil)
|
||||
require.Error(t, err)
|
||||
require.Nil(t, engine)
|
||||
require.Contains(t, err.Error(), "unknown")
|
||||
})
|
||||
|
||||
t.Run("Unknown database type", func(t *testing.T) {
|
||||
@@ -81,13 +97,56 @@ func TestGetEngine(t *testing.T) {
|
||||
dbSection := cfg.SectionWithEnvOverrides("database")
|
||||
dbSection.Key("type").SetValue("unknown")
|
||||
|
||||
engine, err := getEngine(cfg)
|
||||
engine, err := newResourceDBProvider(nil, cfg, nil)
|
||||
require.Error(t, err)
|
||||
require.Nil(t, engine)
|
||||
require.Contains(t, err.Error(), "unsupported database type")
|
||||
require.Contains(t, err.Error(), "unknown")
|
||||
})
|
||||
}
|
||||
|
||||
func TestDatabaseConfigOverridenByEnvVariable(t *testing.T) {
|
||||
prevEnv := os.Environ()
|
||||
t.Cleanup(func() {
|
||||
// Revert env variables to state before this test.
|
||||
os.Clearenv()
|
||||
for _, e := range prevEnv {
|
||||
sp := strings.SplitN(e, "=", 2)
|
||||
if len(sp) == 2 {
|
||||
assert.NoError(t, os.Setenv(sp[0], sp[1]))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
require.NoError(t, os.MkdirAll(filepath.Join(tmpDir, "conf"), 0750))
|
||||
// We need to include database.url in defaults, otherwise it won't be overridden by environment variable!
|
||||
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "conf/defaults.ini"), []byte("[log.console]\nlevel =\n[database]\nurl = \n"), 0644))
|
||||
|
||||
dbConfig := `
|
||||
[database]
|
||||
type = postgres
|
||||
host = localhost
|
||||
name = grafana
|
||||
user = user
|
||||
password = password
|
||||
`
|
||||
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "conf/custom.ini"), []byte(dbConfig), 0644))
|
||||
|
||||
// Override database URL
|
||||
require.NoError(t, os.Setenv("GF_DATABASE_URL", "mysql://gf:pwd@overthere:3306/grafana"))
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
require.NoError(t, cfg.Load(setting.CommandLineArgs{HomePath: tmpDir}))
|
||||
|
||||
engine, err := newResourceDBProvider(nil, cfg, nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, engine)
|
||||
// Verify that GF_DATABASE_URL value is used.
|
||||
require.Equal(t, dbTypeMySQL, engine.engine.Dialect().DriverName())
|
||||
require.Contains(t, engine.engine.DataSourceName(), "overthere:3306")
|
||||
}
|
||||
|
||||
func TestGetEngineMySQLFromConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"xorm.io/xorm"
|
||||
|
||||
infraDB "github.com/grafana/grafana/pkg/infra/db"
|
||||
@@ -31,8 +33,8 @@ const grafanaDBInstrumentQueriesKey = "instrument_queries"
|
||||
var errGrafanaDBInstrumentedNotSupported = errors.New("the Resource API is " +
|
||||
"attempting to leverage the database from core Grafana defined in the" +
|
||||
" [database] INI section since a database configuration was not provided" +
|
||||
" in the [resource_api] section. But we detected that the key `" +
|
||||
grafanaDBInstrumentQueriesKey + "` is enabled in [database], and that" +
|
||||
" in the [resource_api] section. But we detected that the key" +
|
||||
" `instrument_queries` is enabled in [database], and that" +
|
||||
" setup is currently unsupported. Please, consider disabling that flag")
|
||||
|
||||
func ProvideResourceDB(grafanaDB infraDB.DB, cfg *setting.Cfg, tracer trace.Tracer) (db.DBProvider, error) {
|
||||
@@ -76,7 +78,11 @@ func newResourceDBProvider(grafanaDB infraDB.DB, cfg *setting.Cfg, tracer trace.
|
||||
// as fallback, and as it uses a dedicated INI section, then keys are not
|
||||
// prefixed with "db_"
|
||||
getter := newConfGetter(cfg.SectionWithEnvOverrides("resource_api"), "db_")
|
||||
fallbackGetter := newConfGetter(cfg.SectionWithEnvOverrides("database"), "")
|
||||
fallbackConfig, fallbackErr := sqlstore.NewDatabaseConfig(cfg, nil)
|
||||
if fallbackErr != nil {
|
||||
// Ignore error here and keep going.
|
||||
fallbackConfig = nil
|
||||
}
|
||||
|
||||
p = &resourceDBProvider{
|
||||
cfg: cfg,
|
||||
@@ -87,7 +93,6 @@ func newResourceDBProvider(grafanaDB infraDB.DB, cfg *setting.Cfg, tracer trace.
|
||||
}
|
||||
|
||||
dbType := getter.String("type")
|
||||
grafanaDBType := fallbackGetter.String("type")
|
||||
switch {
|
||||
// Deprecated: First try with the config in the "resource_api" section, which is specific to Unified Storage
|
||||
case dbType == dbTypePostgres:
|
||||
@@ -104,19 +109,22 @@ func newResourceDBProvider(grafanaDB infraDB.DB, cfg *setting.Cfg, tracer trace.
|
||||
return p, fmt.Errorf("invalid db type specified: %s", dbType)
|
||||
|
||||
// If we have an empty Resource API db config, try with the core Grafana database config
|
||||
case grafanaDBType != "":
|
||||
case fallbackConfig != nil && fallbackConfig.Type != "":
|
||||
p.registerMetrics = true
|
||||
p.engine, err = getEngine(cfg)
|
||||
p.engine, err = getEngine(fallbackConfig)
|
||||
return p, err
|
||||
case grafanaDB != nil:
|
||||
// try to use the grafana db connection (should only happen in tests)
|
||||
if fallbackGetter.Bool(grafanaDBInstrumentQueriesKey) {
|
||||
if newConfGetter(cfg.SectionWithEnvOverrides("database"), "").Bool(grafanaDBInstrumentQueriesKey) {
|
||||
return nil, errGrafanaDBInstrumentedNotSupported
|
||||
}
|
||||
p.engine = grafanaDB.GetEngine()
|
||||
return p, nil
|
||||
default:
|
||||
return p, fmt.Errorf("no database type specified")
|
||||
if fallbackErr != nil {
|
||||
return nil, fallbackErr
|
||||
}
|
||||
return nil, fmt.Errorf("no database type specified")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user