Skip to content

Commit

Permalink
[FAB-3094] CouchDB Errors if system databases missing
Browse files Browse the repository at this point in the history
CouchDB log fills with errors if its system databases are not created.
The system databases should be created upon peer startup,
if they do not yet exist.

This change makes a simple check on creating the state database to
check if the system database exist as well.  If they are missing, then
the system databases are created.

Change-Id: I6dacb8ceefcda8fd0f14a0ef55da921b9160343d
Signed-off-by: Chris Elder <chris.elder@us.ibm.com>
  • Loading branch information
Chris Elder committed Apr 18, 2017
1 parent 7f336b9 commit 9280d69
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 8 deletions.
25 changes: 20 additions & 5 deletions core/ledger/util/couchdb/couchdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,8 +321,11 @@ func (dbclient *CouchDatabase) GetDatabaseInfo() (*DBInfo, *DBReturn, error) {

}

//VerifyConnection method provides function to verify the connection information
func (couchInstance *CouchInstance) VerifyConnection() (*ConnectionInfo, *DBReturn, error) {
//VerifyCouchConfig method provides function to verify the connection information
func (couchInstance *CouchInstance) VerifyCouchConfig() (*ConnectionInfo, *DBReturn, error) {

logger.Debugf("Entering VerifyCouchConfig()")
defer logger.Debugf("Exiting VerifyCouchConfig()")

connectURL, err := url.Parse(couchInstance.conf.URL)
if err != nil {
Expand All @@ -334,16 +337,18 @@ func (couchInstance *CouchInstance) VerifyConnection() (*ConnectionInfo, *DBRetu
//get the number of retries for startup
maxRetriesOnStartup := couchInstance.conf.MaxRetriesOnStartup

resp, couchDBReturn, err := couchInstance.handleRequest(http.MethodGet, connectURL.String(), nil, "", "", maxRetriesOnStartup)
resp, couchDBReturn, err := couchInstance.handleRequest(http.MethodGet, connectURL.String(), nil,
couchInstance.conf.Username, couchInstance.conf.Password, maxRetriesOnStartup)

if err != nil {
return nil, couchDBReturn, err
return nil, couchDBReturn, fmt.Errorf("Unable to connect to CouchDB, check the hostname and port: %s", err.Error())
}
defer resp.Body.Close()

dbResponse := &ConnectionInfo{}
errJSON := json.NewDecoder(resp.Body).Decode(&dbResponse)
if errJSON != nil {
return nil, nil, errJSON
return nil, nil, fmt.Errorf("Unable to connect to CouchDB, check the hostname and port: %s", errJSON.Error())
}

// trace the database info response
Expand All @@ -354,6 +359,16 @@ func (couchInstance *CouchInstance) VerifyConnection() (*ConnectionInfo, *DBRetu
}
}

//check to see if the system databases exist
//Verifying the existence of the system database accomplishes two steps
//1. Ensures the system databases are created
//2. Verifies the username password provided in the CouchDB config are valid for system admin
err = CreateSystemDatabasesIfNotExist(*couchInstance)
if err != nil {
logger.Errorf("Unable to connect to CouchDB, error: %s Check the admin username and password.\n", err.Error())
return nil, nil, fmt.Errorf("Unable to connect to CouchDB, error: %s Check the admin username and password.\n", err.Error())
}

return dbResponse, couchDBReturn, nil

}
Expand Down
35 changes: 33 additions & 2 deletions core/ledger/util/couchdb/couchdbutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ func CreateCouchInstance(couchDBConnectURL, id, pw string, maxRetries,
//Create the CouchDB instance
couchInstance := &CouchInstance{conf: *couchConf}

connectInfo, retVal, verifyErr := couchInstance.VerifyConnection()
connectInfo, retVal, verifyErr := couchInstance.VerifyCouchConfig()
if verifyErr != nil {
return nil, fmt.Errorf("Unable to connect to CouchDB, check the hostname and port: %s", verifyErr.Error())
return nil, verifyErr
}

//return an error if the http return value is not 200
Expand Down Expand Up @@ -96,6 +96,37 @@ func CreateCouchDatabase(couchInstance CouchInstance, dbName string) (*CouchData
return &couchDBDatabase, nil
}

//CreateSystemDatabasesIfNotExist - creates the system databases if they do not exist
func CreateSystemDatabasesIfNotExist(couchInstance CouchInstance) error {

dbName := "_users"
systemCouchDBDatabase := CouchDatabase{CouchInstance: couchInstance, DBName: dbName}
_, err := systemCouchDBDatabase.CreateDatabaseIfNotExist()
if err != nil {
logger.Errorf("Error during CouchDB CreateDatabaseIfNotExist() for system dbName: %s error: %s\n", dbName, err.Error())
return err
}

dbName = "_replicator"
systemCouchDBDatabase = CouchDatabase{CouchInstance: couchInstance, DBName: dbName}
_, err = systemCouchDBDatabase.CreateDatabaseIfNotExist()
if err != nil {
logger.Errorf("Error during CouchDB CreateDatabaseIfNotExist() for system dbName: %s error: %s\n", dbName, err.Error())
return err
}

dbName = "_global_changes"
systemCouchDBDatabase = CouchDatabase{CouchInstance: couchInstance, DBName: dbName}
_, err = systemCouchDBDatabase.CreateDatabaseIfNotExist()
if err != nil {
logger.Errorf("Error during CouchDB CreateDatabaseIfNotExist() for system dbName: %s error: %s\n", dbName, err.Error())
return err
}

return nil

}

//mapAndValidateDatabaseName checks to see if the database name contains illegal characters
//CouchDB Rules: Only lowercase characters (a-z), digits (0-9), and any of the characters
//_, $, (, ), +, -, and / are allowed. Must begin with a letter.
Expand Down
42 changes: 41 additions & 1 deletion core/ledger/util/couchdb/couchdbutil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (

//Unit test of couch db util functionality
func TestCreateCouchDBConnectionAndDB(t *testing.T) {
if ledgerconfig.IsCouchDBEnabled() == true {
if ledgerconfig.IsCouchDBEnabled() {

database := "testcreatecouchdbconnectionanddb"
cleanup(database)
Expand All @@ -41,6 +41,46 @@ func TestCreateCouchDBConnectionAndDB(t *testing.T) {

}

//Unit test of couch db util functionality
func TestCreateCouchDBSystemDBs(t *testing.T) {
if ledgerconfig.IsCouchDBEnabled() {

database := "testcreatecouchdbsystemdb"
cleanup(database)
defer cleanup(database)

//create a new connection
couchInstance, err := CreateCouchInstance(connectURL, "", "", maxRetries,
maxRetriesOnStartup, requestTimeout)
testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to CreateCouchInstance"))

err = CreateSystemDatabasesIfNotExist(*couchInstance)
testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create system databases"))

db := CouchDatabase{CouchInstance: *couchInstance, DBName: "_users"}

//Retrieve the info for the new database and make sure the name matches
dbResp, _, errdb := db.GetDatabaseInfo()
testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to retrieve _users database information"))
testutil.AssertEquals(t, dbResp.DbName, "_users")

db = CouchDatabase{CouchInstance: *couchInstance, DBName: "_replicator"}

//Retrieve the info for the new database and make sure the name matches
dbResp, _, errdb = db.GetDatabaseInfo()
testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to retrieve _replicator database information"))
testutil.AssertEquals(t, dbResp.DbName, "_replicator")

db = CouchDatabase{CouchInstance: *couchInstance, DBName: "_global_changes"}

//Retrieve the info for the new database and make sure the name matches
dbResp, _, errdb = db.GetDatabaseInfo()
testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to retrieve _global_changes database information"))
testutil.AssertEquals(t, dbResp.DbName, "_global_changes")

}

}
func TestDatabaseMapping(t *testing.T) {

//create a new instance and database object using a database name mixed case
Expand Down

0 comments on commit 9280d69

Please sign in to comment.