github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/core/ledger/util/couchdb/couchdbutil.go (about)

     1  /*
     2  Copyright IBM Corp. 2016 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  		 http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package couchdb
    18  
    19  import (
    20  	"fmt"
    21  	"regexp"
    22  	"strconv"
    23  	"strings"
    24  )
    25  
    26  var validNamePattern = `^[a-z][a-z0-9_$(),+/-]+`
    27  var maxLength = 249
    28  
    29  //CreateCouchInstance creates a CouchDB instance
    30  func CreateCouchInstance(couchDBConnectURL string, id string, pw string) (*CouchInstance, error) {
    31  	couchConf, err := CreateConnectionDefinition(couchDBConnectURL,
    32  		id,
    33  		pw)
    34  	if err != nil {
    35  		logger.Errorf("Error during CouchDB CreateConnectionDefinition(): %s\n", err.Error())
    36  		return nil, err
    37  	}
    38  
    39  	//Create the CouchDB instance
    40  	couchInstance := &CouchInstance{conf: *couchConf}
    41  
    42  	connectInfo, retVal, verifyErr := couchInstance.VerifyConnection()
    43  	if verifyErr != nil {
    44  		return nil, fmt.Errorf("Unable to connect to CouchDB, check the hostname and port: %s", verifyErr.Error())
    45  	}
    46  
    47  	//return an error if the http return value is not 200
    48  	if retVal.StatusCode != 200 {
    49  		return nil, fmt.Errorf("CouchDB connection error, expecting return code of 200, received %v", retVal.StatusCode)
    50  	}
    51  
    52  	//check the CouchDB version number, return an error if the version is not at least 2.0.0
    53  	errVersion := checkCouchDBVersion(connectInfo.Version)
    54  	if errVersion != nil {
    55  		return nil, errVersion
    56  	}
    57  
    58  	return couchInstance, nil
    59  }
    60  
    61  //checkCouchDBVersion verifies CouchDB is at least 2.0.0
    62  func checkCouchDBVersion(version string) error {
    63  
    64  	//split the version into parts
    65  	majorVersion := strings.Split(version, ".")
    66  
    67  	//check to see that the major version number is at least 2
    68  	majorVersionInt, _ := strconv.Atoi(majorVersion[0])
    69  	if majorVersionInt < 2 {
    70  		return fmt.Errorf("CouchDB must be at least version 2.0.0.  Detected version %s", version)
    71  	}
    72  
    73  	return nil
    74  }
    75  
    76  //CreateCouchDatabase creates a CouchDB database object, as well as the underlying database if it does not exist
    77  func CreateCouchDatabase(couchInstance CouchInstance, dbName string) (*CouchDatabase, error) {
    78  
    79  	databaseName, err := mapAndValidateDatabaseName(dbName)
    80  	if err != nil {
    81  		logger.Errorf("Error during CouchDB CreateDatabaseIfNotExist() for dbName: %s  error: %s\n", dbName, err.Error())
    82  		return nil, err
    83  	}
    84  
    85  	couchDBDatabase := CouchDatabase{couchInstance: couchInstance, dbName: databaseName}
    86  
    87  	// Create CouchDB database upon ledger startup, if it doesn't already exist
    88  	_, err = couchDBDatabase.CreateDatabaseIfNotExist()
    89  	if err != nil {
    90  		logger.Errorf("Error during CouchDB CreateDatabaseIfNotExist() for dbName: %s  error: %s\n", dbName, err.Error())
    91  		return nil, err
    92  	}
    93  
    94  	return &couchDBDatabase, nil
    95  }
    96  
    97  //mapAndValidateDatabaseName checks to see if the database name contains illegal characters
    98  //CouchDB Rules: Only lowercase characters (a-z), digits (0-9), and any of the characters
    99  //_, $, (, ), +, -, and / are allowed. Must begin with a letter.
   100  //
   101  //Restictions have already been applied to the database name from Orderer based on
   102  //restrictions required by Kafka
   103  //
   104  //The validation will validate upper case, the string will be lower cased
   105  //Replace any characters not allowed in CouchDB with an "_"
   106  //Check for a leading letter, if not present, the prepend "db_"
   107  func mapAndValidateDatabaseName(databaseName string) (string, error) {
   108  
   109  	// test Length
   110  	if len(databaseName) <= 0 {
   111  		return "", fmt.Errorf("Database name is illegal, cannot be empty")
   112  	}
   113  	if len(databaseName) > maxLength {
   114  		return "", fmt.Errorf("Database name is illegal, cannot be longer than %d", maxLength)
   115  	}
   116  
   117  	//force the name to all lowercase
   118  	databaseName = strings.ToLower(databaseName)
   119  
   120  	//Replace any characters not allowed in CouchDB with an "_"
   121  	replaceString := regexp.MustCompile(`[^a-z0-9_$(),+/-]`)
   122  
   123  	//Set up the replace pattern for special characters
   124  	validatedDatabaseName := replaceString.ReplaceAllString(databaseName, "_")
   125  
   126  	//if the first character is not a letter, then prepend "db_"
   127  	testLeadingLetter := regexp.MustCompile("^[a-z]")
   128  	isLeadingLetter := testLeadingLetter.MatchString(validatedDatabaseName)
   129  	if !isLeadingLetter {
   130  		validatedDatabaseName = "db_" + validatedDatabaseName
   131  	}
   132  
   133  	//create the expression for valid characters
   134  	validString := regexp.MustCompile(validNamePattern)
   135  
   136  	// Illegal characters
   137  	matched := validString.MatchString(validatedDatabaseName)
   138  	if !matched {
   139  		return "", fmt.Errorf("Database name '%s' contains illegal characters", validatedDatabaseName)
   140  	}
   141  	return validatedDatabaseName, nil
   142  }