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