Skip to content

Commit

Permalink
[FAB-2662] Implement CouchDB docker config
Browse files Browse the repository at this point in the history
https://jira.hyperledger.org/browse/FAB-2662

Changing the CouchDB dockers used in fabric to  use recommended
configuration (couchdb local.ini).

Include config comments in local.ini stating how to enable
user security.

Also, making sure that all CouchDB unit-tests follow the same procedure
for setting up address and login credentials.  Including test
comments stating how to run tests against CouchDB with
user security enabled.

Change-Id: I975d04d757d0371c8db03acf0ddcf92c01c35f8c
Signed-off-by: Adnan Choudhury <adnan.choudhury@itpeoplecorp.com>
Signed-off-by: denyeart <enyeart@us.ibm.com>
  • Loading branch information
adnan-c authored and denyeart committed Apr 20, 2017
1 parent eba4a20 commit 668b4c3
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 45 deletions.
2 changes: 1 addition & 1 deletion core/chaincode/exectransaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ func finitPeer(lis net.Listener, chainIDs ...string) {
requestTimeout := viper.GetDuration("ledger.state.couchDBConfig.requestTimeout")

couchInstance, _ := couchdb.CreateCouchInstance(connectURL, username, password, maxRetries, maxRetriesOnStartup, requestTimeout)
db, _ := couchdb.CreateCouchDatabase(*couchInstance, chainID)
db := couchdb.CouchDatabase{CouchInstance: *couchInstance, DBName: chainID}
//drop the test database
db.DropDatabase()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package statecouchdb
import (
"os"
"testing"
"time"

"github.com/hyperledger/fabric/common/ledger/testutil"
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb"
Expand All @@ -31,15 +32,28 @@ import (

func TestMain(m *testing.M) {

//call a helper method to load the core.yaml, will be used to detect if CouchDB is enabled
// Read the core.yaml file for default config.
ledgertestutil.SetupCoreYAMLConfig("./../../../../../../peer")

viper.Set("peer.fileSystemPath", "/tmp/fabric/ledgertests/kvledger/txmgmt/statedb/statecouchdb")

// Switch to CouchDB
viper.Set("ledger.state.stateDatabase", "CouchDB")

// both vagrant and CI have couchdb configured at host "couchdb"
viper.Set("ledger.state.couchDBConfig.couchDBAddress", "couchdb:5984")

// Replace with correct username/password such as
// admin/admin if user security is enabled on couchdb.
viper.Set("ledger.state.couchDBConfig.username", "")
viper.Set("ledger.state.couchDBConfig.password", "")
viper.Set("ledger.state.couchDBConfig.maxRetries", 3)
viper.Set("ledger.state.couchDBConfig.maxRetriesOnStartup", 10)
viper.Set("ledger.state.couchDBConfig.requestTimeout", time.Second*20)

//run the actual test
result := m.Run()

//revert to default goleveldb
viper.Set("ledger.state.stateDatabase", "goleveldb")
os.Exit(result)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,13 @@ package statecouchdb
import (
"strings"
"testing"
"time"

"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb"
"github.com/hyperledger/fabric/core/ledger/ledgerconfig"
"github.com/hyperledger/fabric/core/ledger/util/couchdb"
)

//Basic setup to test couch
var connectURL = "couchdb:5984"
var badConnectURL = "couchdb:5990"
var username = ""
var password = ""
var maxRetries = 3
var maxRetriesOnStartup = 10
var connectionTimeout = time.Second * 60
const badConnectURL = "couchdb:5990"

// TestVDBEnv provides a couch db backed versioned db for testing
type TestVDBEnv struct {
Expand All @@ -59,7 +52,9 @@ func (env *TestVDBEnv) Cleanup(dbName string) {
}
func cleanupDB(dbName string) {
//create a new connection
couchInstance, _ := couchdb.CreateCouchInstance(connectURL, username, password, maxRetries, maxRetriesOnStartup, connectionTimeout)
couchDBDef := ledgerconfig.GetCouchDBDefinition()
couchInstance, _ := couchdb.CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
db := couchdb.CouchDatabase{CouchInstance: *couchInstance, DBName: dbName}
//drop the test database
db.DropDatabase()
Expand Down
8 changes: 8 additions & 0 deletions core/ledger/kvledger/txmgmt/txmgr/commontests/pkg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package commontests

import (
"testing"
"time"

"github.com/hyperledger/fabric/common/ledger/testutil"
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb"
Expand Down Expand Up @@ -106,6 +107,13 @@ func (env *couchDBLockBasedEnv) init(t *testing.T, testLedgerID string) {
viper.Set("peer.fileSystemPath", testFilesystemPath)
// both vagrant and CI have couchdb configured at host "couchdb"
viper.Set("ledger.state.couchDBConfig.couchDBAddress", "couchdb:5984")
// Replace with correct username/password such as
// admin/admin if user security is enabled on couchdb.
viper.Set("ledger.state.couchDBConfig.username", "")
viper.Set("ledger.state.couchDBConfig.password", "")
viper.Set("ledger.state.couchDBConfig.maxRetries", 3)
viper.Set("ledger.state.couchDBConfig.maxRetriesOnStartup", 10)
viper.Set("ledger.state.couchDBConfig.requestTimeout", time.Second*20)
testDBEnv := statecouchdb.NewTestVDBEnv(t)
testDB, err := testDBEnv.DBProvider.GetDBHandle(testLedgerID)
testutil.AssertNoError(t, err, "")
Expand Down
93 changes: 62 additions & 31 deletions core/ledger/util/couchdb/couchdb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,17 @@ import (
"github.com/spf13/viper"
)

//Basic setup to test couch
var connectURL = "couchdb:5984"
var badConnectURL = "couchdb:5990"
var username = ""
var password = ""
var maxRetries = 3
var maxRetriesOnStartup = 10
var requestTimeout = time.Second * 20

const badConnectURL = "couchdb:5990"
const updateDocumentConflictError = "conflict"
const updateDocumentConflictReason = "Document update conflict."

var couchDBDef *ledgerconfig.CouchDBDef

func cleanup(database string) error {
//create a new connection
couchInstance, err := CreateCouchInstance(connectURL, username, password, maxRetries, maxRetriesOnStartup, requestTimeout)
couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)

if err != nil {
fmt.Println("Unexpected error", err)
return err
Expand All @@ -68,28 +64,47 @@ type Asset struct {
var assetJSON = []byte(`{"asset_name":"marble1","color":"blue","size":"35","owner":"jerry"}`)

func TestMain(m *testing.M) {
// Read the core.yaml file for default config.
ledgertestutil.SetupCoreYAMLConfig("./../../../../peer")

// Switch to CouchDB
viper.Set("ledger.state.stateDatabase", "CouchDB")

// both vagrant and CI have couchdb configured at host "couchdb"
viper.Set("ledger.state.couchDBConfig.couchDBAddress", "couchdb:5984")
// Replace with correct username/password such as
// admin/admin if user security is enabled on couchdb.
viper.Set("ledger.state.couchDBConfig.username", "")
viper.Set("ledger.state.couchDBConfig.password", "")
viper.Set("ledger.state.couchDBConfig.maxRetries", 3)
viper.Set("ledger.state.couchDBConfig.maxRetriesOnStartup", 10)
viper.Set("ledger.state.couchDBConfig.requestTimeout", time.Second*20)

// Create CouchDB definition from config parameters
couchDBDef = ledgerconfig.GetCouchDBDefinition()

//run the tests
result := m.Run()

//revert to default goleveldb
viper.Set("ledger.state.stateDatabase", "goleveldb")
os.Exit(result)
}

func TestDBConnectionDef(t *testing.T) {

//call a helper method to load the core.yaml
ledgertestutil.SetupCoreYAMLConfig("./../../../../peer")

//create a new connection
_, err := CreateConnectionDefinition(connectURL, "", "", maxRetries, maxRetriesOnStartup, requestTimeout)
_, err := CreateConnectionDefinition(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create database connection definition"))

}

func TestDBBadConnectionDef(t *testing.T) {

//create a new connection
_, err := CreateConnectionDefinition("^^^localhost:5984", "", "", maxRetries, maxRetriesOnStartup, requestTimeout)
_, err := CreateConnectionDefinition("^^^localhost:5984", couchDBDef.Username, couchDBDef.Password,
couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
testutil.AssertError(t, err, fmt.Sprintf("Did not receive error when trying to create database connection definition with a bad hostname"))

}
Expand All @@ -105,7 +120,8 @@ func TestDBCreateSaveWithoutRevision(t *testing.T) {

if err == nil {
//create a new instance and database object
couchInstance, err := CreateCouchInstance(connectURL, username, password, maxRetries, maxRetriesOnStartup, requestTimeout)
couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}

Expand All @@ -131,7 +147,8 @@ func TestDBCreateEnsureFullCommit(t *testing.T) {

if err == nil {
//create a new instance and database object
couchInstance, err := CreateCouchInstance(connectURL, username, password, maxRetries, maxRetriesOnStartup, requestTimeout)
couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}

Expand All @@ -156,25 +173,29 @@ func TestDBBadDatabaseName(t *testing.T) {
if ledgerconfig.IsCouchDBEnabled() {

//create a new instance and database object using a valid database name mixed case
couchInstance, err := CreateCouchInstance(connectURL, username, password, maxRetries, maxRetriesOnStartup, requestTimeout)
couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
_, dberr := CreateCouchDatabase(*couchInstance, "testDB")
testutil.AssertNoError(t, dberr, fmt.Sprintf("Error when testing a valid database name"))

//create a new instance and database object using a valid database name letters and numbers
couchInstance, err = CreateCouchInstance(connectURL, username, password, maxRetries, maxRetriesOnStartup, requestTimeout)
couchInstance, err = CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
_, dberr = CreateCouchDatabase(*couchInstance, "test132")
testutil.AssertNoError(t, dberr, fmt.Sprintf("Error when testing a valid database name"))

//create a new instance and database object using a valid database name - special characters
couchInstance, err = CreateCouchInstance(connectURL, username, password, maxRetries, maxRetriesOnStartup, requestTimeout)
couchInstance, err = CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
_, dberr = CreateCouchDatabase(*couchInstance, "test1234~!@#$%^&*()[]{}.")
testutil.AssertNoError(t, dberr, fmt.Sprintf("Error when testing a valid database name"))

//create a new instance and database object using a invalid database name - too long /*
couchInstance, err = CreateCouchInstance(connectURL, username, password, maxRetries, maxRetriesOnStartup, requestTimeout)
couchInstance, err = CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
_, dberr = CreateCouchDatabase(*couchInstance, "A12345678901234567890123456789012345678901234"+
"56789012345678901234567890123456789012345678901234567890123456789012345678901234567890"+
Expand All @@ -195,7 +216,8 @@ func TestDBBadConnection(t *testing.T) {
if ledgerconfig.IsCouchDBEnabled() {

//create a new instance and database object
_, err := CreateCouchInstance(badConnectURL, username, password, maxRetries, maxRetriesOnStartup, requestTimeout)
_, err := CreateCouchInstance(badConnectURL, couchDBDef.Username, couchDBDef.Password,
couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
testutil.AssertError(t, err, fmt.Sprintf("Error should have been thrown for a bad connection"))
}
}
Expand All @@ -211,7 +233,8 @@ func TestDBCreateDatabaseAndPersist(t *testing.T) {

if err == nil {
//create a new instance and database object
couchInstance, err := CreateCouchInstance(connectURL, username, password, maxRetries, maxRetriesOnStartup, requestTimeout)
couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}

Expand Down Expand Up @@ -305,7 +328,8 @@ func TestDBRequestTimeout(t *testing.T) {

//create a new instance and database object with a timeout that will fail
//Also use a maxRetriesOnStartup=3 to reduce the number of retries
_, err := CreateCouchInstance(connectURL, username, password, maxRetries, 3, impossibleTimeout)
_, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
couchDBDef.MaxRetries, 3, impossibleTimeout)
testutil.AssertError(t, err, fmt.Sprintf("Error should have been thown while trying to create a couchdb instance with a connection timeout"))

//see if the error message contains the timeout error
Expand All @@ -327,7 +351,8 @@ func TestDBBadJSON(t *testing.T) {
if err == nil {

//create a new instance and database object
couchInstance, err := CreateCouchInstance(connectURL, username, password, maxRetries, maxRetriesOnStartup, requestTimeout)
couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}

Expand Down Expand Up @@ -363,7 +388,8 @@ func TestPrefixScan(t *testing.T) {

if err == nil {
//create a new instance and database object
couchInstance, err := CreateCouchInstance(connectURL, username, password, maxRetries, maxRetriesOnStartup, requestTimeout)
couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}

Expand Down Expand Up @@ -435,7 +461,8 @@ func TestDBSaveAttachment(t *testing.T) {
attachments = append(attachments, attachment)

//create a new instance and database object
couchInstance, err := CreateCouchInstance(connectURL, username, password, maxRetries, maxRetriesOnStartup, requestTimeout)
couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}

Expand Down Expand Up @@ -468,7 +495,8 @@ func TestDBDeleteDocument(t *testing.T) {

if err == nil {
//create a new instance and database object
couchInstance, err := CreateCouchInstance(connectURL, username, password, maxRetries, maxRetriesOnStartup, requestTimeout)
couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}

Expand Down Expand Up @@ -506,7 +534,8 @@ func TestDBDeleteNonExistingDocument(t *testing.T) {

if err == nil {
//create a new instance and database object
couchInstance, err := CreateCouchInstance(connectURL, username, password, maxRetries, maxRetriesOnStartup, requestTimeout)
couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}

Expand Down Expand Up @@ -645,7 +674,8 @@ func TestRichQuery(t *testing.T) {

if err == nil {
//create a new instance and database object --------------------------------------------------------
couchInstance, err := CreateCouchInstance(connectURL, username, password, maxRetries, maxRetriesOnStartup, requestTimeout)
couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}

Expand Down Expand Up @@ -859,7 +889,8 @@ func TestBatchBatchOperations(t *testing.T) {
defer cleanup(database)

//create a new instance and database object --------------------------------------------------------
couchInstance, err := CreateCouchInstance(connectURL, username, password, maxRetries, maxRetriesOnStartup, requestTimeout)
couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}

Expand Down
3 changes: 2 additions & 1 deletion core/ledger/util/couchdb/couchdbutil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ func TestCreateCouchDBConnectionAndDB(t *testing.T) {
cleanup(database)
defer cleanup(database)
//create a new connection
couchInstance, err := CreateCouchInstance(connectURL, "", "", maxRetries, maxRetriesOnStartup, requestTimeout)
couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to CreateCouchInstance"))

_, err = CreateCouchDatabase(*couchInstance, database)
Expand Down
44 changes: 44 additions & 0 deletions images/couchdb/local.ini
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,47 @@
[chttpd]
bind_address = 0.0.0.0

[couchdb]
; Specify the location of the database in container.
; Optionally, these directories can be mounted in the host via docker.
database_dir = /opt/couchdb/data/
view_index_dir = /opt/couchdb/data/
uri_file = /opt/couchdb/data/couch.uri

; only allow the admin user to connect
; Uncomment the following statement to enable admin user security.
; default_security = admin_only

; allow delayed commits since peer manages savepoints and flushing to disk
delayed_commits = true

[cluster]
; peer maintains a single replica
n = 1

; adjust q to set the level of parallelism locally
; recommended to have no more than 10 million documents/shard (q)
; for 100 million documents, q=10 -- at a minimum
q = 8

[log]
writer = file
file = /opt/couchdb/logs/couchdb.log
level = info

; Uncomment the following two statements to enable admin user security.
; [httpd]
; www-authenticate = Basic realm="administrator"

[couch_httpd_auth]
; Uncomment the following statement to enable admin user security.
; require_valid_user = true
iterations = 1000 ; iterations for password hashing

; Uncomment the following two statements to enable admin user security.
; [admins]
; admin = admin

[attachments]
compressible_types = text/*, application/javascript, application/json, application/xml, application/octet-stream

0 comments on commit 668b4c3

Please sign in to comment.