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  }