github.com/kyma-project/kyma-environment-broker@v0.0.1/internal/storage/postsql/init.go (about) 1 package postsql 2 3 import ( 4 "fmt" 5 "regexp" 6 "time" 7 8 "github.com/gocraft/dbr" 9 10 "github.com/lib/pq" 11 "github.com/sirupsen/logrus" 12 ) 13 14 const ( 15 schemaName = "public" 16 InstancesTableName = "instances" 17 OperationTableName = "operations" 18 OrchestrationTableName = "orchestrations" 19 RuntimeStateTableName = "runtime_states" 20 CreatedAtField = "created_at" 21 ) 22 23 // InitializeDatabase opens database connection and initializes schema if it does not exist 24 func InitializeDatabase(connectionURL string, retries int, log logrus.FieldLogger) (*dbr.Connection, error) { 25 connection, err := WaitForDatabaseAccess(connectionURL, retries, 300*time.Millisecond, log) 26 if err != nil { 27 return nil, err 28 } 29 30 initialized, err := CheckIfDatabaseInitialized(connection) 31 if err != nil { 32 closeDBConnection(connection, log) 33 return nil, fmt.Errorf("failed to check if database is initialized: %w", err) 34 } 35 if initialized { 36 log.Info("Database already initialized") 37 return connection, nil 38 } 39 40 return connection, nil 41 } 42 43 func closeDBConnection(db *dbr.Connection, log logrus.FieldLogger) { 44 err := db.Close() 45 if err != nil { 46 log.Warnf("Failed to close database connection: %s", err.Error()) 47 } 48 } 49 50 const TableNotExistsError = "42P01" 51 52 func CheckIfDatabaseInitialized(db *dbr.Connection) (bool, error) { 53 checkQuery := fmt.Sprintf(`SELECT '%s.%s'::regclass;`, schemaName, InstancesTableName) 54 55 row := db.QueryRow(checkQuery) 56 57 var tableName string 58 err := row.Scan(&tableName) 59 60 if err != nil { 61 psqlErr, converted := err.(*pq.Error) 62 63 if converted && psqlErr.Code == TableNotExistsError { 64 return false, nil 65 } 66 67 return false, fmt.Errorf("failed to check if database is initialized: %w", err) 68 } 69 70 return tableName == InstancesTableName, nil 71 } 72 73 func WaitForDatabaseAccess(connString string, retryCount int, sleepTime time.Duration, log logrus.FieldLogger) (*dbr.Connection, error) { 74 var connection *dbr.Connection 75 var err error 76 77 re := regexp.MustCompile(`password=.*?\s`) 78 log.Info(re.ReplaceAllString(connString, "")) 79 80 for ; retryCount > 0; retryCount-- { 81 connection, err = dbr.Open("postgres", connString, nil) 82 if err != nil { 83 return nil, fmt.Errorf("invalid connection string: %w", err) 84 } 85 86 err = connection.Ping() 87 if err == nil { 88 return connection, nil 89 } 90 log.Warnf("Database Connection failed: %s", err.Error()) 91 92 err = connection.Close() 93 if err != nil { 94 log.Info("Failed to close database ...") 95 } 96 97 log.Infof("Failed to access database, waiting %v to retry...", sleepTime) 98 time.Sleep(sleepTime) 99 } 100 101 return nil, fmt.Errorf("timeout waiting for database access") 102 }