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 }