Remove support for Google Spanner database. (#105846)

* Remove support for Google Spanner database.
This commit is contained in:
Peter Štibraný
2025-05-23 11:35:59 +02:00
committed by GitHub
parent 9769871a88
commit c4d3eb1cd0
50 changed files with 26 additions and 1265 deletions
+1 -127
View File
@@ -9,18 +9,9 @@ import (
"encoding/hex"
"fmt"
"os"
"strconv"
"testing"
"time"
database "cloud.google.com/go/spanner/admin/database/apiv1"
"cloud.google.com/go/spanner/admin/database/apiv1/databasepb"
"cloud.google.com/go/spanner/spannertest"
spannerdriver "github.com/googleapis/go-sql-spanner"
"google.golang.org/api/option"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/registry"
@@ -269,9 +260,6 @@ func createTemporaryDatabase(tb TestingTB) (*testDB, error) {
// SQLite doesn't have a concept of a database server, so we always create a new file with no connections required.
return newSQLite3DB(tb)
}
if dbType == "spanner" {
return newSpannerDB(tb)
}
// On the remaining databases, we first connect to the configured credentials, create a new database, then return this new database's info as a connection string.
// We use databases rather than schemas as MySQL has no concept of schemas, so this aligns them more closely.
@@ -326,121 +314,7 @@ func createTemporaryDatabase(tb TestingTB) (*testDB, error) {
func generateDatabaseName() string {
// The database name has to be unique amongst all tests. It is highly unlikely we will have a collision here.
// The database name has to be <= 64 chars long on MySQL, and <= 31 chars on Postgres.
// Database ID length on Spanner must be between 2 and 30 characters. (https://cloud.google.com/spanner/quotas#database-limits)
return "grafana_test_" + randomLowerHex(17)
}
func newSpannerDB(tb TestingTB) (*testDB, error) {
// See https://github.com/googleapis/go-sql-spanner/blob/main/driver.go#L56-L81 for connection string options.
spannerDB := env("SPANNER_DB", "emulator")
if spannerDB == "spannertest" {
// Start new in-memory spannertest instance. This is mostly useless for our tests
// (spannertest doesn't support many things that we use), but added for completion.
// Each spannertest instance is a separate db.
srv, err := spannertest.NewServer("localhost:0")
if err != nil {
return nil, err
}
tb.Cleanup(srv.Close)
return &testDB{
Driver: "spanner",
Conn: fmt.Sprintf("%s/projects/grafanatest/instances/grafanatest/databases/grafanatest;usePlainText=true", srv.Addr),
}, nil
}
conn := spannerDB
if spannerDB == "emulator" {
host := env("SPANNER_EMULATOR_HOST", "localhost:9010")
conn = fmt.Sprintf("%s/projects/grafanatest/instances/grafanatest/databases/grafanatest;usePlainText=true", host)
}
cfg, err := spannerdriver.ExtractConnectorConfig(conn)
if err != nil {
return nil, err
}
clientOptions := spannerConnectorConfigToClientOptions(cfg)
dbname := generateDatabaseName()
fullDbName := fmt.Sprintf("projects/%s/instances/%s/databases/%s", cfg.Project, cfg.Instance, dbname)
dbCreated := false
databaseAdminClient, err := database.NewDatabaseAdminClient(tb.Context(), clientOptions...)
if err != nil {
return nil, fmt.Errorf("failed to create database admin client: %v", err)
}
tb.Cleanup(func() {
if dbCreated {
// Drop database in the cleanup.
// Can't use tb.Context() here, since that is canceled before calling Cleanup functions.
err := databaseAdminClient.DropDatabase(context.Background(), &databasepb.DropDatabaseRequest{
Database: fullDbName,
})
if err != nil {
tb.Logf("Failed to drop Spanner database %s due to error %v", fullDbName, err)
} else {
tb.Logf("Dropped temporary Spanner database %s", fullDbName)
}
}
_ = databaseAdminClient.Close()
})
op, err := databaseAdminClient.CreateDatabase(tb.Context(), &databasepb.CreateDatabaseRequest{
Parent: fmt.Sprintf("projects/%s/instances/%s", cfg.Project, cfg.Instance),
CreateStatement: fmt.Sprintf("CREATE DATABASE `%s`", dbname),
DatabaseDialect: databasepb.DatabaseDialect_GOOGLE_STANDARD_SQL,
})
if err != nil {
return nil, fmt.Errorf("failed to create database: %v", err)
}
_, err = op.Wait(tb.Context())
if err != nil {
return nil, fmt.Errorf("failed to create database: %v", err)
}
tb.Logf("Created temporary Spanner database %s", fullDbName)
dbCreated = true
// Rebuild connection string, but change database to ID of just-created database.
// Example: `localhost:9010/projects/test-project/instances/test-instance/databases/test-database;usePlainText=true;disableRouteToLeader=true;enableEndToEndTracing=true`
connString := ""
if cfg.Host != "" {
connString = fmt.Sprintf("%s/", cfg.Host)
}
// Use new DB name instead of cfg.Database.
connString = connString + fmt.Sprintf("projects/%s/instances/%s/databases/%s", cfg.Project, cfg.Instance, dbname)
for k, v := range cfg.Params {
connString = connString + fmt.Sprintf(";%s=%s", k, v)
}
return &testDB{
Driver: "spanner",
Conn: connString,
}, nil
}
// This is same code as xorm.SpannerConnectorConfigToClientOptions, but we cannot use that because it's under "enterprise" build tag.
func spannerConnectorConfigToClientOptions(connectorConfig spannerdriver.ConnectorConfig) []option.ClientOption {
var opts []option.ClientOption
if connectorConfig.Host != "" {
opts = append(opts, option.WithEndpoint(connectorConfig.Host))
}
if strval, ok := connectorConfig.Params["credentials"]; ok {
opts = append(opts, option.WithCredentialsFile(strval))
}
if strval, ok := connectorConfig.Params["credentialsjson"]; ok {
opts = append(opts, option.WithCredentialsJSON([]byte(strval)))
}
if strval, ok := connectorConfig.Params["useplaintext"]; ok {
if val, err := strconv.ParseBool(strval); err == nil && val {
opts = append(opts,
option.WithGRPCDialOption(grpc.WithTransportCredentials(insecure.NewCredentials())),
option.WithoutAuthentication())
}
}
return opts
return "grafana_test_" + randomLowerHex(18)
}
func env(name, fallback string) string {