Skip to content

Commit

Permalink
METAL-3490 Fix database connection string for generic databases (#140)
Browse files Browse the repository at this point in the history
  • Loading branch information
allanger committed May 10, 2022
1 parent aefb00f commit 0ce3aec
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 47 deletions.
11 changes: 2 additions & 9 deletions controllers/database_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -605,16 +605,9 @@ func (r *DatabaseReconciler) createConnectionString(ctx context.Context, dbcr *k
if err != nil {
return err
}
// Fill ConnectionStringFields struct
dbUrl := ConnectionStringFields{
DatabaseHost: dbcr.Status.ProxyStatus.ServiceName,
DatabasePort: dbcr.Status.ProxyStatus.SQLPort,
UserName: dbcr.Status.UserName,
Password: databaseCred.Password,
DatabaseName: dbcr.Status.DatabaseName,
}

// Generate the connection string
dbConnectionString, err := generateConnectionString(dbcr, dbUrl)
dbConnectionString, err := generateConnectionString(dbcr, databaseCred)
if err != nil {
return err
}
Expand Down
41 changes: 30 additions & 11 deletions controllers/database_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,17 +79,17 @@ func determinDatabaseType(dbcr *kciv1alpha1.Database, dbCred database.Credential
}

db := database.Postgres{
Backend: backend,
Host: host,
Port: uint16(port),
Database: dbCred.Name,
User: dbCred.Username,
Password: dbCred.Password,
Extensions: extList,
SSLEnabled: instance.Spec.SSLConnection.Enabled,
SkipCAVerify: instance.Spec.SSLConnection.SkipVerify,
Backend: backend,
Host: host,
Port: uint16(port),
Database: dbCred.Name,
User: dbCred.Username,
Password: dbCred.Password,
Extensions: extList,
SSLEnabled: instance.Spec.SSLConnection.Enabled,
SkipCAVerify: instance.Spec.SSLConnection.SkipVerify,
DropPublicSchema: dbcr.Spec.Postgres.DropPublicSchema,
Schemas: dbcr.Spec.Postgres.Schemas,
Schemas: dbcr.Spec.Postgres.Schemas,
}

return db, nil
Expand Down Expand Up @@ -209,11 +209,30 @@ func generateDatabaseSecretData(dbcr *kciv1alpha1.Database) (map[string][]byte,
}
}

func generateConnectionString(dbcr *kciv1alpha1.Database, dbData ConnectionStringFields) (connString string, err error) {
func generateConnectionString(dbcr *kciv1alpha1.Database, databaseCred database.Credentials) (connString string, err error) {
// The string that's going to be generated if the default template is used:
// "postgresql://user:password@host:port/database"
const defaultTemplate = "{{ .Protocol }}://{{ .UserName }}:{{ .Password }}@{{ .DatabaseHost }}:{{ .DatabasePort }}/{{ .DatabaseName }}"

dbData := ConnectionStringFields{
DatabaseHost: dbcr.Status.ProxyStatus.ServiceName,
DatabasePort: dbcr.Status.ProxyStatus.SQLPort,
UserName: databaseCred.Username,
Password: databaseCred.Password,
DatabaseName: databaseCred.Name,
}

// If proxy is not used, set a real database address
if !dbcr.Status.ProxyStatus.Status {
db, err := determinDatabaseType(dbcr, databaseCred)
if err != nil {
return "", err
}
dbAddress := db.GetDatabaseAddress()
dbData.DatabaseHost = dbAddress.Host
dbData.DatabasePort = int32(dbAddress.Port)
}

// If engine is 'postgres', the protocol should be postgresql
if dbcr.Status.InstanceRef.Spec.Engine == "postgres" {
dbData.Protocol = "postgresql"
Expand Down
71 changes: 45 additions & 26 deletions controllers/database_helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,42 +120,69 @@ func TestMonitoringEnabled(t *testing.T) {
assert.Equal(t, found, true, "expected pg_stat_statement is included in extension list")
}

func TestPsqlDefaultConnectionStringGeneratation(t *testing.T) {
func TestPsqlDefaultConnectionStringGeneratationWithProxy(t *testing.T) {
instance := newPostgresTestDbInstanceCr()
postgresDbCr := newPostgresTestDbCr(instance)
postgresDbCr.Status.ProxyStatus.Status = true

c := ConnectionStringFields{
DatabaseHost: "postgres",
DatabasePort: 5432,
UserName: "root",
Password: "qwertyu9",
DatabaseName: "postgres",
UserName: testDbcred.Username,
Password: testDbcred.Password,
DatabaseName: testDbcred.Name,
}

postgresDbCr.Status.ProxyStatus.SQLPort = c.DatabasePort
postgresDbCr.Status.ProxyStatus.ServiceName = c.DatabaseHost

protocol := "postgresql"
expectedString := fmt.Sprintf("%s://%s:%s@%s:%d/%s", protocol, c.UserName, c.Password, c.DatabaseHost, c.DatabasePort, c.DatabaseName)

connString, err := generateConnectionString(postgresDbCr, c)
connString, err := generateConnectionString(postgresDbCr, testDbcred)
if err != nil {
t.Logf("Unexpected error: %s", err)
t.Fail()
}
assert.Equal(t, connString, expectedString, "generated connections string is wrong")
assert.Equal(t, expectedString, connString, "generated connections string is wrong")
}

func TestMysqlDefaultConnectionStringGeneratation(t *testing.T) {
mysqlDbCr := newMysqlTestDbCr()
func TestPsqlDefaultConnectionStringGeneratationWithoutProxy(t *testing.T) {
instance := newPostgresTestDbInstanceCr()
postgresDbCr := newPostgresTestDbCr(instance)

c := ConnectionStringFields{
DatabaseHost: "mysql",
DatabaseHost: "postgres",
DatabasePort: 5432,
UserName: "root",
Password: "qwertyu9",
DatabaseName: "mysql",
UserName: testDbcred.Username,
Password: testDbcred.Password,
DatabaseName: testDbcred.Name,
}

protocol := "postgresql"
expectedString := fmt.Sprintf("%s://%s:%s@%s:%d/%s", protocol, c.UserName, c.Password, c.DatabaseHost, c.DatabasePort, c.DatabaseName)

connString, err := generateConnectionString(postgresDbCr, testDbcred)
if err != nil {
t.Logf("Unexpected error: %s", err)
t.Fail()
}
assert.Equal(t, expectedString, connString, "generated connections string is wrong")
}

func TestMysqlDefaultConnectionStringGeneratationWithoutProxy(t *testing.T) {
mysqlDbCr := newMysqlTestDbCr()
c := ConnectionStringFields{
DatabaseHost: "mysql",
DatabasePort: 3306,
UserName: testDbcred.Username,
Password: testDbcred.Password,
DatabaseName: testDbcred.Name,
}
protocol := "mysql"
expectedString := fmt.Sprintf("%s://%s:%s@%s:%d/%s", protocol, c.UserName, c.Password, c.DatabaseHost, c.DatabasePort, c.DatabaseName)

connString, err := generateConnectionString(mysqlDbCr, c)
connString, err := generateConnectionString(mysqlDbCr, testDbcred)
if err != nil {
t.Logf("Unexpected error: %s", err)
t.Fail()
Expand Down Expand Up @@ -193,14 +220,14 @@ func TestPsqlCustomConnectionStringGeneratation(t *testing.T) {
c := ConnectionStringFields{
DatabaseHost: "postgres",
DatabasePort: 5432,
UserName: "root",
Password: "qwertyu9",
DatabaseName: "postgres",
UserName: testDbcred.Username,
Password: testDbcred.Password,
DatabaseName: testDbcred.Name,
}
protocol := "postgresql"
expectedString := fmt.Sprintf("%s%s://%s:%s@%s:%d/%s%s", prefix, protocol, c.UserName, c.Password, c.DatabaseHost, c.DatabasePort, c.DatabaseName, postfix)

connString, err := generateConnectionString(postgresDbCr, c)
connString, err := generateConnectionString(postgresDbCr, testDbcred)
if err != nil {
t.Logf("unexpected error: %s", err)
t.Fail()
Expand All @@ -214,15 +241,7 @@ func TestWrongTemplateConnectionStringGeneratation(t *testing.T) {

postgresDbCr.Spec.ConnectionStringTemplate = "{{ .Protocol }}://{{ .User }}:{{ .Password }}@{{ .DatabaseHost }}:{{ .DatabasePort }}/{{ .DatabaseName }}"

c := ConnectionStringFields{
DatabaseHost: "localhost",
DatabasePort: 5432,
UserName: "root",
Password: "qwertyu9",
DatabaseName: "postgres",
}

_, err := generateConnectionString(postgresDbCr, c)
_, err := generateConnectionString(postgresDbCr, testDbcred)
errSubstr := "can't evaluate field User in type controllers.ConnectionStringFields"

assert.Contains(t, err.Error(), errSubstr, "the error doesn't contain expected substring")
Expand Down
3 changes: 2 additions & 1 deletion controllers/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const (
func newPostgresTestDbInstanceCr() kciv1alpha1.DbInstance {
info := make(map[string]string)
info["DB_PORT"] = "5432"

info["DB_CONN"] = "postgres"
return kciv1alpha1.DbInstance{
Spec: kciv1alpha1.DbInstanceSpec{
Engine: "postgres",
Expand Down Expand Up @@ -75,6 +75,7 @@ func newMysqlTestDbCr() *kciv1alpha1.Database {

info := make(map[string]string)
info["DB_PORT"] = "3306"
info["DB_CONN"] = "mysql"

db := kciv1alpha1.Database{
ObjectMeta: o,
Expand Down
7 changes: 7 additions & 0 deletions pkg/utils/database/mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,13 @@ func (m Mysql) GetCredentials() Credentials {
}
}

func (m Mysql) GetDatabaseAddress() DatabaseAddress {
return DatabaseAddress{
Host: m.Host,
Port: m.Port,
}
}

// ParseAdminCredentials parse admin username and password of mysql database from secret data
// If "user" key is not defined, take "root" as admin user by default
func (m Mysql) ParseAdminCredentials(data map[string][]byte) (AdminCredentials, error) {
Expand Down
7 changes: 7 additions & 0 deletions pkg/utils/database/postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,13 @@ func (p Postgres) checkExtensions() error {
return nil
}


func (p Postgres) GetDatabaseAddress() DatabaseAddress {
return DatabaseAddress {
Host: p.Host,
Port: p.Port,
}
}
// ParseAdminCredentials parse admin username and password of postgres database from secret data
// If "user" key is not defined, take "postgres" as admin user by default
func (p Postgres) ParseAdminCredentials(data map[string][]byte) (AdminCredentials, error) {
Expand Down
7 changes: 7 additions & 0 deletions pkg/utils/database/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ type Credentials struct {
ConnectionString string
}

// DatabaseAddress contains host and port of a database instance
type DatabaseAddress struct {
Host string
Port uint16
}

// AdminCredentials contains admin username and password of database server
type AdminCredentials struct {
Username string `yaml:"user"`
Expand All @@ -39,4 +45,5 @@ type Database interface {
CheckStatus() error
GetCredentials() Credentials
ParseAdminCredentials(data map[string][]byte) (AdminCredentials, error)
GetDatabaseAddress() DatabaseAddress
}

0 comments on commit 0ce3aec

Please sign in to comment.