Files
Peter Štibraný 4efdef9b9c Switch to using in-mem sequence generator only when explicitly configured in connection string. Move spanner-related functions to pkg/util/spanner. (#103363)
* Switch to using in-mem sequence generator only when explicitly configured in connection string.
Move spanner-related functions to pkg/util/spanner.
2025-04-08 16:36:37 +02:00

205 lines
5.0 KiB
Go

package sqlutil
import (
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
"cloud.google.com/go/spanner/spannertest"
)
// ITestDB is an interface of arguments for testing db
type ITestDB interface {
Helper()
Fatalf(format string, args ...any)
Logf(format string, args ...any)
Log(args ...any)
Cleanup(func())
Skipf(format string, args ...any)
}
type TestDB struct {
DriverName string
ConnStr string
Path string
Cleanup func()
}
func GetTestDBType() string {
dbType := "sqlite3"
// environment variable present for test db?
if db, present := os.LookupEnv("GRAFANA_TEST_DB"); present {
dbType = db
}
return dbType
}
func GetTestDB(dbType string) (*TestDB, error) {
switch dbType {
case "mysql":
return mySQLTestDB()
case "postgres":
return postgresTestDB()
case "sqlite3":
return sqLite3TestDB()
case "spanner":
return spannerTestDB()
}
return nil, fmt.Errorf("unknown test db type: %s", dbType)
}
func sqLite3TestDB() (*TestDB, error) {
if os.Getenv("SQLITE_INMEMORY") == "true" {
return &TestDB{
DriverName: "sqlite3",
ConnStr: "file::memory:",
Cleanup: func() {},
}, nil
}
ret := &TestDB{
DriverName: "sqlite3",
Cleanup: func() {},
}
sqliteDb := os.Getenv("SQLITE_TEST_DB")
if sqliteDb == "" {
// try to create a database file in the user's cache directory
dir, err := os.UserCacheDir()
if err != nil {
return nil, err
}
// if cache dir doesn't exist, fall back to temp dir
if _, err := os.Stat(dir); errors.Is(err, fs.ErrNotExist) {
dir = os.TempDir()
if _, err := os.Stat(dir); err != nil {
return nil, err
}
}
err = os.Mkdir(filepath.Join(dir, "grafana-test"), 0750)
if err != nil && !errors.Is(err, fs.ErrExist) {
return nil, err
}
f, err := os.CreateTemp(filepath.Join(dir, "grafana-test"), "grafana-test-*.db")
if err != nil {
return nil, err
}
sqliteDb = f.Name()
ret.Cleanup = func() {
// remove db file if it exists
err := os.Remove(sqliteDb)
if err != nil && !errors.Is(err, fs.ErrNotExist) {
fmt.Printf("Error removing sqlite db file %s: %v\n", sqliteDb, err)
}
// remove wal & shm files if they exist
err = os.Remove(sqliteDb + "-wal")
if err != nil && !errors.Is(err, fs.ErrNotExist) {
fmt.Printf("Error removing sqlite wal file %s: %v\n", sqliteDb+"-wal", err)
}
err = os.Remove(sqliteDb + "-shm")
if err != nil && !errors.Is(err, fs.ErrNotExist) {
fmt.Printf("Error removing sqlite shm file %s: %v\n", sqliteDb+"-shm", err)
}
}
}
ret.ConnStr = "file:" + sqliteDb + "?cache=private&mode=rwc"
if os.Getenv("SQLITE_JOURNAL_MODE") != "false" {
// For tests, set sync=OFF for faster commits. Reference: https://www.sqlite.org/pragma.html#pragma_synchronous.
ret.ConnStr += "&_journal_mode=WAL&_synchronous=OFF"
}
ret.Path = sqliteDb
return ret, nil
}
func mySQLTestDB() (*TestDB, error) {
host := os.Getenv("MYSQL_HOST")
if host == "" {
host = "localhost"
}
port := os.Getenv("MYSQL_PORT")
if port == "" {
port = "3306"
}
conn_str := fmt.Sprintf("grafana:password@tcp(%s:%s)/grafana_tests?collation=utf8mb4_unicode_ci&sql_mode='ANSI_QUOTES'&parseTime=true", host, port)
return &TestDB{
DriverName: "mysql",
ConnStr: conn_str,
Cleanup: func() {},
}, nil
}
func postgresTestDB() (*TestDB, error) {
host := os.Getenv("POSTGRES_HOST")
if host == "" {
host = "localhost"
}
port := os.Getenv("POSTGRES_PORT")
if port == "" {
port = "5432"
}
connStr := fmt.Sprintf("user=grafanatest password=grafanatest host=%s port=%s dbname=grafanatest sslmode=disable", host, port)
return &TestDB{
DriverName: "postgres",
ConnStr: connStr,
Cleanup: func() {},
}, nil
}
func spannerTestDB() (*TestDB, error) {
// See https://github.com/googleapis/go-sql-spanner/blob/main/driver.go#L56-L81 for connection string options.
spannerDB := os.Getenv("SPANNER_DB")
if spannerDB == "" {
spannerDB = "emulator"
}
if spannerDB == "spannertest" {
// Start in-memory spannertest instance.
srv, err := spannertest.NewServer("localhost:0")
if err != nil {
return nil, err
}
return &TestDB{
DriverName: "spanner",
ConnStr: fmt.Sprintf("%s/projects/grafanatest/instances/grafanatest/databases/grafanatest;usePlainText=true", srv.Addr),
Cleanup: srv.Close,
}, nil
}
if spannerDB == "emulator" {
host := os.Getenv("SPANNER_EMULATOR_HOST")
if host == "" {
host = "localhost:9010"
}
// To create instance and database manually, run:
//
// $ curl "localhost:9020/v1/projects/grafanatest/instances" --data '{"instanceId": "'grafanatest'"}'
// $ curl "localhost:9020/v1/projects/grafanatest/instances/grafanatest/databases" --data '{"createStatement": "CREATE DATABASE `grafanatest`"}'
return &TestDB{
DriverName: "spanner",
ConnStr: fmt.Sprintf("%s/projects/grafanatest/instances/grafanatest/databases/grafanatest;usePlainText=true;inMemSequenceGenerator=true", host),
Cleanup: func() {},
}, nil
}
return &TestDB{
DriverName: "spanner",
ConnStr: spannerDB,
Cleanup: func() {},
}, nil
}