github.com/adecaro/fabric-ca@v2.0.0-alpha+incompatible/lib/server/db/sqlite/sqlite.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package sqlite
     8  
     9  import (
    10  	"context"
    11  	"database/sql"
    12  	"strings"
    13  
    14  	"github.com/cloudflare/cfssl/log"
    15  	"github.com/hyperledger/fabric-ca/lib/server/db"
    16  	"github.com/hyperledger/fabric/common/metrics"
    17  	"github.com/jmoiron/sqlx"
    18  	"github.com/pkg/errors"
    19  )
    20  
    21  //go:generate counterfeiter -o mocks/create.go -fake-name Create . Create
    22  
    23  // Create is interface that defines functions need to create database transaction
    24  type Create interface {
    25  	Exec(funcName, query string, args ...interface{}) (sql.Result, error)
    26  	Rebind(query string) string
    27  	Rollback(funcName string) error
    28  	Commit(funcName string) error
    29  }
    30  
    31  // Sqlite defines SQLite database
    32  type Sqlite struct {
    33  	SqlxDB          db.FabricCADB
    34  	CreateTx        Create
    35  	CAName          string
    36  	MetricsProvider metrics.Provider
    37  
    38  	datasource string
    39  }
    40  
    41  // NewDB creates a SQLite database
    42  func NewDB(datasource, caName string, metricsProvider metrics.Provider) *Sqlite {
    43  	log.Debugf("Using sqlite database, connect to database in home (%s) directory", datasource)
    44  	return &Sqlite{
    45  		datasource:      datasource,
    46  		CAName:          caName,
    47  		MetricsProvider: metricsProvider,
    48  	}
    49  }
    50  
    51  // Connect connects to a SQLite database
    52  func (s *Sqlite) Connect() error {
    53  	var err error
    54  	log.Debugf("Creating SQLite database (%s) if it does not exist...", s.datasource)
    55  	sqlxDB, err := sqlx.Connect("sqlite3", s.datasource+"?_busy_timeout=5000")
    56  	if err != nil {
    57  		return errors.Wrap(err, "Failed to open sqlite3 DB")
    58  	}
    59  	s.SqlxDB = db.New(sqlxDB, s.CAName, s.MetricsProvider)
    60  	return nil
    61  }
    62  
    63  // PingContext pings the database
    64  func (s *Sqlite) PingContext(ctx context.Context) error {
    65  	err := s.SqlxDB.PingContext(ctx)
    66  	if err != nil {
    67  		return errors.Wrap(err, "Failed to ping to SQLite database")
    68  	}
    69  	return nil
    70  }
    71  
    72  // Create creates database and tables
    73  func (s *Sqlite) Create() (*db.DB, error) {
    74  	s.CreateTx = s.SqlxDB.BeginTx()
    75  	err := s.CreateTables()
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  	return s.SqlxDB.(*db.DB), nil
    80  }
    81  
    82  // CreateTables creates table
    83  func (s *Sqlite) CreateTables() error {
    84  	err := s.doTransaction("CreateTable", createAllSQLiteTables)
    85  	if err != nil {
    86  		return err
    87  	}
    88  
    89  	// Set maximum open connections to one. This is to share one connection
    90  	// across multiple go routines. This will serialize database operations
    91  	// with in a single server there by preventing "database is locked"
    92  	// error under load. The "Database is locked" error is still expected
    93  	// when multiple servers are accessing the same database (but mitigated
    94  	// by specifying _busy_timeout to 5 seconds). Since sqlite is
    95  	// for development and test purposes only, and is not recommended to
    96  	// be used in a clustered topology, setting max open connections to
    97  	// 1 is a quick and effective solution
    98  	// For more info refer to https://github.com/mattn/go-sqlite3/issues/274
    99  	log.Debug("Successfully opened sqlite3 DB")
   100  	s.SqlxDB.SetMaxOpenConns(1)
   101  
   102  	return nil
   103  }
   104  
   105  func createAllSQLiteTables(tx Create, args ...interface{}) error {
   106  	err := createIdentityTable(tx)
   107  	if err != nil {
   108  		return err
   109  	}
   110  	err = createAffiliationTable(tx)
   111  	if err != nil {
   112  		return err
   113  	}
   114  	err = createCertificateTable(tx)
   115  	if err != nil {
   116  		return err
   117  	}
   118  	err = createCredentialsTable(tx)
   119  	if err != nil {
   120  		return err
   121  	}
   122  	err = createRevocationComponentTable(tx)
   123  	if err != nil {
   124  		return err
   125  	}
   126  	err = createNoncesTable(tx)
   127  	if err != nil {
   128  		return err
   129  	}
   130  	err = createPropertiesTable(tx)
   131  	if err != nil {
   132  		return err
   133  	}
   134  	return nil
   135  }
   136  
   137  func createIdentityTable(tx Create) error {
   138  	log.Debug("Creating users table if it does not exist")
   139  	if _, err := tx.Exec("CreateUsersTable", "CREATE TABLE IF NOT EXISTS users (id VARCHAR(255), token bytea, type VARCHAR(256), affiliation VARCHAR(1024), attributes TEXT, state INTEGER,  max_enrollments INTEGER, level INTEGER DEFAULT 0, incorrect_password_attempts INTEGER DEFAULT 0)"); err != nil {
   140  		return errors.Wrap(err, "Error creating users table")
   141  	}
   142  	return nil
   143  }
   144  
   145  func createAffiliationTable(tx Create) error {
   146  	log.Debug("Creating affiliations table if it does not exist")
   147  	if _, err := tx.Exec("CreateAffiliationsTable", "CREATE TABLE IF NOT EXISTS affiliations (name VARCHAR(1024) NOT NULL UNIQUE, prekey VARCHAR(1024), level INTEGER DEFAULT 0)"); err != nil {
   148  		return errors.Wrap(err, "Error creating affiliations table")
   149  	}
   150  	return nil
   151  }
   152  
   153  func createCertificateTable(tx Create) error {
   154  	log.Debug("Creating certificates table if it does not exist")
   155  	if _, err := tx.Exec("CreateCertificatesTable", "CREATE TABLE IF NOT EXISTS certificates (id VARCHAR(255), serial_number blob NOT NULL, authority_key_identifier blob NOT NULL, ca_label blob, status blob NOT NULL, reason int, expiry timestamp, revoked_at timestamp, pem blob NOT NULL, level INTEGER DEFAULT 0, PRIMARY KEY(serial_number, authority_key_identifier))"); err != nil {
   156  		return errors.Wrap(err, "Error creating certificates table")
   157  	}
   158  	return nil
   159  }
   160  
   161  func createCredentialsTable(tx Create) error {
   162  	log.Debug("Creating credentials table if it does not exist")
   163  	if _, err := tx.Exec("CreateCredentialsTable", "CREATE TABLE IF NOT EXISTS credentials (id VARCHAR(255), revocation_handle blob NOT NULL, cred blob NOT NULL, ca_label blob, status blob NOT NULL, reason int, expiry timestamp, revoked_at timestamp, level INTEGER DEFAULT 0, PRIMARY KEY(revocation_handle))"); err != nil {
   164  		return errors.Wrap(err, "Error creating credentials table")
   165  	}
   166  	return nil
   167  }
   168  
   169  func createRevocationComponentTable(tx Create) error {
   170  	log.Debug("Creating revocation_authority_info table if it does not exist")
   171  	if _, err := tx.Exec("CreateRevocationAuthorityTable", "CREATE TABLE IF NOT EXISTS revocation_authority_info (epoch INTEGER, next_handle INTEGER, lasthandle_in_pool INTEGER, level INTEGER DEFAULT 0, PRIMARY KEY(epoch))"); err != nil {
   172  		return errors.Wrap(err, "Error creating revocation_authority_info table")
   173  	}
   174  	return nil
   175  }
   176  
   177  func createNoncesTable(tx Create) error {
   178  	log.Debug("Creating nonces table if it does not exist")
   179  	if _, err := tx.Exec("CreateNoncesTable", "CREATE TABLE IF NOT EXISTS nonces (val VARCHAR(1024) NOT NULL UNIQUE, expiry timestamp, level INTEGER DEFAULT 0, PRIMARY KEY(val))"); err != nil {
   180  		return errors.Wrap(err, "Error creating nonces table")
   181  	}
   182  	return nil
   183  }
   184  
   185  func createPropertiesTable(tx Create) error {
   186  	log.Debug("Creating properties table if it does not exist")
   187  	_, err := tx.Exec("CreatePropertiesTable", "CREATE TABLE IF NOT EXISTS properties (property VARCHAR(255), value VARCHAR(256), PRIMARY KEY(property))")
   188  	if err != nil {
   189  		return errors.Wrap(err, "Error creating properties table")
   190  	}
   191  	_, err = tx.Exec("CreatePropertiesTable", tx.Rebind("INSERT INTO properties (property, value) VALUES ('identity.level', '0'), ('affiliation.level', '0'), ('certificate.level', '0'), ('credential.level', '0'), ('rcinfo.level', '0'), ('nonce.level', '0')"))
   192  	if err != nil {
   193  		if !strings.Contains(err.Error(), "UNIQUE constraint failed") {
   194  			return errors.Wrap(err, "Failed to initialize properties table")
   195  		}
   196  	}
   197  	return nil
   198  }
   199  
   200  func (s *Sqlite) doTransaction(funcName string, doit func(tx Create, args ...interface{}) error, args ...interface{}) error {
   201  	tx := s.CreateTx
   202  	err := doit(tx, args...)
   203  	if err != nil {
   204  		err2 := tx.Rollback(funcName)
   205  		if err2 != nil {
   206  			log.Errorf("Error encountered while rolling back transaction: %s", err2)
   207  			return err
   208  		}
   209  		return err
   210  	}
   211  
   212  	err = tx.Commit(funcName)
   213  	if err != nil {
   214  		return errors.Wrap(err, "Error encountered while committing transaction")
   215  	}
   216  	return nil
   217  }