Actually use --driver option to set database driver for drone.

This includes refactoring database setup, and migration system.

Remove setupDatabase from main and use `Init` method from database
package.

At database package, defines Init method which actually initiate
database with options given from comand line flag. I think `--path`
wont be used anywhere so I plan to remove it later.

Both meddler and migration initiated here, then we call `Set` method
to setup all the tables, etc. Here I think I want to separate database
schema and turn it into migration script instead, later maybe.

At migration package I made some tweak to `Operation` interface.
Realized that it's ludicrous to let migration driver re-implement `Exec`
and `Query`, I made migration script to receive The whole
migrationDriver struct which contains both Operation implementor, and
the Tx itself. This made possible thanks to Go struct being able to
promote its member, now our migration is more transparent.

There's also stub implementation for bot mysql and postgresql, will
implement this really soon.
This commit is contained in:
Nurahmadie 2014-03-08 12:19:28 +07:00
parent ef908ebb94
commit 51519b62dd
10 changed files with 203 additions and 109 deletions

View file

@ -25,6 +25,8 @@ deps:
go get github.com/drone/go-bitbucket/bitbucket go get github.com/drone/go-bitbucket/bitbucket
go get github.com/GeertJohan/go.rice go get github.com/GeertJohan/go.rice
go get github.com/GeertJohan/go.rice/rice go get github.com/GeertJohan/go.rice/rice
go get github.com/go-sql-driver/mysql
go get github.com/lib/pq
go get github.com/mattn/go-sqlite3 go get github.com/mattn/go-sqlite3
go get github.com/russross/meddler go get github.com/russross/meddler

View file

@ -1,7 +1,6 @@
package main package main
import ( import (
"database/sql"
"flag" "flag"
"log" "log"
"net/http" "net/http"
@ -12,13 +11,10 @@ import (
"code.google.com/p/go.net/websocket" "code.google.com/p/go.net/websocket"
"github.com/GeertJohan/go.rice" "github.com/GeertJohan/go.rice"
"github.com/bmizerany/pat" "github.com/bmizerany/pat"
_ "github.com/mattn/go-sqlite3"
"github.com/russross/meddler"
"github.com/drone/drone/pkg/build/docker" "github.com/drone/drone/pkg/build/docker"
"github.com/drone/drone/pkg/channel" "github.com/drone/drone/pkg/channel"
"github.com/drone/drone/pkg/database" "github.com/drone/drone/pkg/database"
"github.com/drone/drone/pkg/database/migrate"
"github.com/drone/drone/pkg/handler" "github.com/drone/drone/pkg/handler"
"github.com/drone/drone/pkg/queue" "github.com/drone/drone/pkg/queue"
) )
@ -66,7 +62,9 @@ func main() {
checkTLSFlags() checkTLSFlags()
// setup database and handlers // setup database and handlers
setupDatabase() if err := database.Init(driver, datasource); err != nil {
log.Fatal("Can't initialize database:", err)
}
setupStatic() setupStatic()
setupHandlers() setupHandlers()
@ -92,25 +90,6 @@ func checkTLSFlags() {
} }
// setup the database connection and register with the
// global database package.
func setupDatabase() {
// inform meddler and migration we're using sqlite
meddler.Default = meddler.SQLite
migrate.Driver = migrate.SQLite
// connect to the SQLite database
db, err := sql.Open(driver, datasource)
if err != nil {
log.Fatal(err)
}
database.Set(db)
migration := migrate.New(db)
migration.All().Migrate()
}
// setup routes for static assets. These assets may // setup routes for static assets. These assets may
// be directly embedded inside the application using // be directly embedded inside the application using
// the `rice embed` command, else they are served from disk. // the `rice embed` command, else they are served from disk.

View file

@ -4,12 +4,53 @@ import (
"database/sql" "database/sql"
"log" "log"
"github.com/drone/drone/pkg/database/migrate"
"github.com/drone/drone/pkg/database/schema" "github.com/drone/drone/pkg/database/schema"
_ "github.com/go-sql-driver/mysql"
_ "github.com/lib/pq"
_ "github.com/mattn/go-sqlite3"
"github.com/russross/meddler"
) )
// global instance of our database connection. // global instance of our database connection.
var db *sql.DB var db *sql.DB
func Init(name, datasource string) error {
driver := map[string]struct {
Md *meddler.Database
Mg migrate.DriverFunction
}{
"sqlite3": {
meddler.SQLite,
migrate.SQLite,
},
"mysql": {
meddler.MySQL,
migrate.MySQL,
},
"postgresql": {
meddler.PostgreSQL,
migrate.PostgreSQL,
},
}
meddler.Default = driver[name].Md
migrate.Driver = driver[name].Mg
db, err := sql.Open(name, datasource)
if err != nil {
return err
}
Set(db)
migration := migrate.New(db)
migration.All().Migrate()
return nil
}
// Set sets the default database. // Set sets the default database.
func Set(database *sql.DB) { func Set(database *sql.DB) {
// set the global database // set the global database

View file

@ -8,15 +8,15 @@ func (r *Rev1) Revision() int64 {
return 201402200603 return 201402200603
} }
func (r *Rev1) Up(op Operation) error { func (r *Rev1) Up(mg *MigrationDriver) error {
_, err := op.RenameColumns("repos", map[string]string{ _, err := mg.RenameColumns("repos", map[string]string{
"priveleged": "privileged", "priveleged": "privileged",
}) })
return err return err
} }
func (r *Rev1) Down(op Operation) error { func (r *Rev1) Down(mg *MigrationDriver) error {
_, err := op.RenameColumns("repos", map[string]string{ _, err := mg.RenameColumns("repos", map[string]string{
"privileged": "priveleged", "privileged": "priveleged",
}) })
return err return err

View file

@ -8,19 +8,19 @@ func (r *Rev3) Revision() int64 {
return 201402211147 return 201402211147
} }
func (r *Rev3) Up(op Operation) error { func (r *Rev3) Up(mg *MigrationDriver) error {
_, err := op.AddColumn("settings", "github_domain VARCHAR(255)") _, err := mg.AddColumn("settings", "github_domain VARCHAR(255)")
if err != nil { if err != nil {
return err return err
} }
_, err = op.AddColumn("settings", "github_apiurl VARCHAR(255)") _, err = mg.AddColumn("settings", "github_apiurl VARCHAR(255)")
op.Exec("update settings set github_domain=?", "github.com") mg.Tx.Exec("update settings set github_domain=?", "github.com")
op.Exec("update settings set github_apiurl=?", "https://api.github.com") mg.Tx.Exec("update settings set github_apiurl=?", "https://api.github.com")
return err return err
} }
func (r *Rev3) Down(op Operation) error { func (r *Rev3) Down(mg *MigrationDriver) error {
_, err := op.DropColumns("settings", []string{"github_domain", "github_apiurl"}) _, err := mg.DropColumns("settings", []string{"github_domain", "github_apiurl"})
return err return err
} }

View file

@ -64,22 +64,17 @@ type Operation interface {
DropColumns(tableName string, columnsToDrop []string) (sql.Result, error) DropColumns(tableName string, columnsToDrop []string) (sql.Result, error)
RenameColumns(tableName string, columnChanges map[string]string) (sql.Result, error) RenameColumns(tableName string, columnChanges map[string]string) (sql.Result, error)
Exec(query string, args ...interface{}) (sql.Result, error)
Query(query string, args ...interface{}) (*sql.Rows, error)
QueryRow(query string, args ...interface{}) *sql.Row
} }
type Revision interface { type Revision interface {
Up(op Operation) error Up(mg *MigrationDriver) error
Down(op Operation) error Down(mg *MigrationDriver) error
Revision() int64 Revision() int64
} }
type MigrationDriver struct { type MigrationDriver struct {
Tx *sql.Tx Tx *sql.Tx
Operation
} }
type Migration struct { type Migration struct {
@ -87,7 +82,9 @@ type Migration struct {
revs []Revision revs []Revision
} }
var Driver func(tx *sql.Tx) Operation type DriverFunction func(tx *sql.Tx) *MigrationDriver
var Driver DriverFunction
func New(db *sql.DB) *Migration { func New(db *sql.DB) *Migration {
return &Migration{db: db} return &Migration{db: db}
@ -148,14 +145,14 @@ func (m *Migration) up(target, current int64) error {
return err return err
} }
op := Driver(tx) mg := Driver(tx)
// loop through and execute revisions // loop through and execute revisions
for _, rev := range m.revs { for _, rev := range m.revs {
if rev.Revision() > current && rev.Revision() <= target { if rev.Revision() > current && rev.Revision() <= target {
current = rev.Revision() current = rev.Revision()
// execute the revision Upgrade. // execute the revision Upgrade.
if err := rev.Up(op); err != nil { if err := rev.Up(mg); err != nil {
log.Printf("Failed to upgrade to Revision Number %v\n", current) log.Printf("Failed to upgrade to Revision Number %v\n", current)
log.Println(err) log.Println(err)
return tx.Rollback() return tx.Rollback()
@ -181,7 +178,7 @@ func (m *Migration) down(target, current int64) error {
return err return err
} }
op := Driver(tx) mg := Driver(tx)
// reverse the list of revisions // reverse the list of revisions
revs := []Revision{} revs := []Revision{}
@ -195,7 +192,7 @@ func (m *Migration) down(target, current int64) error {
if rev.Revision() > target { if rev.Revision() > target {
current = rev.Revision() current = rev.Revision()
// execute the revision Upgrade. // execute the revision Upgrade.
if err := rev.Down(op); err != nil { if err := rev.Down(mg); err != nil {
log.Printf("Failed to downgrade from Revision Number %v\n", current) log.Printf("Failed to downgrade from Revision Number %v\n", current)
log.Println(err) log.Println(err)
return tx.Rollback() return tx.Rollback()

View file

@ -0,0 +1,41 @@
package migrate
import (
"database/sql"
"errors"
)
type mysqlDriver struct {
Tx *sql.Tx
}
func MySQL(tx *sql.Tx) *MigrationDriver {
return &MigrationDriver{
Tx: tx,
Operation: &mysqlDriver{Tx: tx},
}
}
func (p *mysqlDriver) CreateTable(tableName string, args []string) (sql.Result, error) {
return nil, errors.New("not implemented yet")
}
func (p *mysqlDriver) RenameTable(tableName, newName string) (sql.Result, error) {
return nil, errors.New("not implemented yet")
}
func (p *mysqlDriver) DropTable(tableName string) (sql.Result, error) {
return nil, errors.New("not implemented yet")
}
func (p *mysqlDriver) AddColumn(tableName, columnSpec string) (sql.Result, error) {
return nil, errors.New("not implemented yet")
}
func (p *mysqlDriver) DropColumns(tableName string, columnsToDrop []string) (sql.Result, error) {
return nil, errors.New("not implemented yet")
}
func (p *mysqlDriver) RenameColumns(tableName string, columnChanges map[string]string) (sql.Result, error) {
return nil, errors.New("not implemented yet")
}

View file

@ -0,0 +1,41 @@
package migrate
import (
"database/sql"
"errors"
)
type postgresqlDriver struct {
Tx *sql.Tx
}
func PostgreSQL(tx *sql.Tx) *MigrationDriver {
return &MigrationDriver{
Tx: tx,
Operation: &postgresqlDriver{Tx: tx},
}
}
func (p *postgresqlDriver) CreateTable(tableName string, args []string) (sql.Result, error) {
return nil, errors.New("not implemented yet")
}
func (p *postgresqlDriver) RenameTable(tableName, newName string) (sql.Result, error) {
return nil, errors.New("not implemented yet")
}
func (p *postgresqlDriver) DropTable(tableName string) (sql.Result, error) {
return nil, errors.New("not implemented yet")
}
func (p *postgresqlDriver) AddColumn(tableName, columnSpec string) (sql.Result, error) {
return nil, errors.New("not implemented yet")
}
func (p *postgresqlDriver) DropColumns(tableName string, columnsToDrop []string) (sql.Result, error) {
return nil, errors.New("not implemented yet")
}
func (p *postgresqlDriver) RenameColumns(tableName string, columnChanges map[string]string) (sql.Result, error) {
return nil, errors.New("not implemented yet")
}

View file

@ -9,41 +9,34 @@ import (
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
) )
type SQLiteDriver MigrationDriver type sqliteDriver struct {
Tx *sql.Tx
func SQLite(tx *sql.Tx) Operation {
return &SQLiteDriver{Tx: tx}
} }
func (s *SQLiteDriver) Exec(query string, args ...interface{}) (sql.Result, error) { func SQLite(tx *sql.Tx) *MigrationDriver {
return s.Tx.Exec(query, args...) return &MigrationDriver{
Tx: tx,
Operation: &sqliteDriver{Tx: tx},
}
} }
func (s *SQLiteDriver) Query(query string, args ...interface{}) (*sql.Rows, error) { func (s *sqliteDriver) CreateTable(tableName string, args []string) (sql.Result, error) {
return s.Tx.Query(query, args...)
}
func (s *SQLiteDriver) QueryRow(query string, args ...interface{}) *sql.Row {
return s.Tx.QueryRow(query, args...)
}
func (s *SQLiteDriver) CreateTable(tableName string, args []string) (sql.Result, error) {
return s.Tx.Exec(fmt.Sprintf("CREATE TABLE %s (%s);", tableName, strings.Join(args, ", "))) return s.Tx.Exec(fmt.Sprintf("CREATE TABLE %s (%s);", tableName, strings.Join(args, ", ")))
} }
func (s *SQLiteDriver) RenameTable(tableName, newName string) (sql.Result, error) { func (s *sqliteDriver) RenameTable(tableName, newName string) (sql.Result, error) {
return s.Tx.Exec(fmt.Sprintf("ALTER TABLE %s RENAME TO %s;", tableName, newName)) return s.Tx.Exec(fmt.Sprintf("ALTER TABLE %s RENAME TO %s;", tableName, newName))
} }
func (s *SQLiteDriver) DropTable(tableName string) (sql.Result, error) { func (s *sqliteDriver) DropTable(tableName string) (sql.Result, error) {
return s.Tx.Exec(fmt.Sprintf("DROP TABLE IF EXISTS %s;", tableName)) return s.Tx.Exec(fmt.Sprintf("DROP TABLE IF EXISTS %s;", tableName))
} }
func (s *SQLiteDriver) AddColumn(tableName, columnSpec string) (sql.Result, error) { func (s *sqliteDriver) AddColumn(tableName, columnSpec string) (sql.Result, error) {
return s.Tx.Exec(fmt.Sprintf("ALTER TABLE %s ADD COLUMN %s;", tableName, columnSpec)) return s.Tx.Exec(fmt.Sprintf("ALTER TABLE %s ADD COLUMN %s;", tableName, columnSpec))
} }
func (s *SQLiteDriver) DropColumns(tableName string, columnsToDrop []string) (sql.Result, error) { func (s *sqliteDriver) DropColumns(tableName string, columnsToDrop []string) (sql.Result, error) {
var err error var err error
var result sql.Result var result sql.Result
@ -144,7 +137,7 @@ func (s *SQLiteDriver) DropColumns(tableName string, columnsToDrop []string) (sq
return result, err return result, err
} }
func (s *SQLiteDriver) RenameColumns(tableName string, columnChanges map[string]string) (sql.Result, error) { func (s *sqliteDriver) RenameColumns(tableName string, columnChanges map[string]string) (sql.Result, error) {
var err error var err error
var result sql.Result var result sql.Result
@ -243,7 +236,7 @@ func (s *SQLiteDriver) RenameColumns(tableName string, columnChanges map[string]
return result, err return result, err
} }
func (s *SQLiteDriver) getDDLFromTable(tableName string) (string, error) { func (s *sqliteDriver) getDDLFromTable(tableName string) (string, error) {
var sql string var sql string
query := `SELECT sql FROM sqlite_master WHERE type='table' and name=?;` query := `SELECT sql FROM sqlite_master WHERE type='table' and name=?;`
err := s.Tx.QueryRow(query, tableName).Scan(&sql) err := s.Tx.QueryRow(query, tableName).Scan(&sql)
@ -253,7 +246,7 @@ func (s *SQLiteDriver) getDDLFromTable(tableName string) (string, error) {
return sql, nil return sql, nil
} }
func (s *SQLiteDriver) getDDLFromIndex(tableName string) ([]string, error) { func (s *sqliteDriver) getDDLFromIndex(tableName string) ([]string, error) {
var sqls []string var sqls []string
query := `SELECT sql FROM sqlite_master WHERE type='index' and tbl_name=?;` query := `SELECT sql FROM sqlite_master WHERE type='index' and tbl_name=?;`

View file

@ -33,8 +33,8 @@ type AddColumnSample struct {
type revision1 struct{} type revision1 struct{}
func (r *revision1) Up(op Operation) error { func (r *revision1) Up(mg *MigrationDriver) error {
_, err := op.CreateTable("samples", []string{ _, err := mg.CreateTable("samples", []string{
"id INTEGER PRIMARY KEY AUTOINCREMENT", "id INTEGER PRIMARY KEY AUTOINCREMENT",
"imel VARCHAR(255) UNIQUE", "imel VARCHAR(255) UNIQUE",
"name VARCHAR(255)", "name VARCHAR(255)",
@ -42,8 +42,8 @@ func (r *revision1) Up(op Operation) error {
return err return err
} }
func (r *revision1) Down(op Operation) error { func (r *revision1) Down(mg *MigrationDriver) error {
_, err := op.DropTable("samples") _, err := mg.DropTable("samples")
return err return err
} }
@ -57,13 +57,13 @@ func (r *revision1) Revision() int64 {
type revision2 struct{} type revision2 struct{}
func (r *revision2) Up(op Operation) error { func (r *revision2) Up(mg *MigrationDriver) error {
_, err := op.RenameTable("samples", "examples") _, err := mg.RenameTable("samples", "examples")
return err return err
} }
func (r *revision2) Down(op Operation) error { func (r *revision2) Down(mg *MigrationDriver) error {
_, err := op.RenameTable("examples", "samples") _, err := mg.RenameTable("examples", "samples")
return err return err
} }
@ -77,16 +77,16 @@ func (r *revision2) Revision() int64 {
type revision3 struct{} type revision3 struct{}
func (r *revision3) Up(op Operation) error { func (r *revision3) Up(mg *MigrationDriver) error {
if _, err := op.AddColumn("samples", "url VARCHAR(255)"); err != nil { if _, err := mg.AddColumn("samples", "url VARCHAR(255)"); err != nil {
return err return err
} }
_, err := op.AddColumn("samples", "num INTEGER") _, err := mg.AddColumn("samples", "num INTEGER")
return err return err
} }
func (r *revision3) Down(op Operation) error { func (r *revision3) Down(mg *MigrationDriver) error {
_, err := op.DropColumns("samples", []string{"num", "url"}) _, err := mg.DropColumns("samples", []string{"num", "url"})
return err return err
} }
@ -100,15 +100,15 @@ func (r *revision3) Revision() int64 {
type revision4 struct{} type revision4 struct{}
func (r *revision4) Up(op Operation) error { func (r *revision4) Up(mg *MigrationDriver) error {
_, err := op.RenameColumns("samples", map[string]string{ _, err := mg.RenameColumns("samples", map[string]string{
"imel": "email", "imel": "email",
}) })
return err return err
} }
func (r *revision4) Down(op Operation) error { func (r *revision4) Down(mg *MigrationDriver) error {
_, err := op.RenameColumns("samples", map[string]string{ _, err := mg.RenameColumns("samples", map[string]string{
"email": "imel", "email": "imel",
}) })
return err return err
@ -124,13 +124,13 @@ func (r *revision4) Revision() int64 {
type revision5 struct{} type revision5 struct{}
func (r *revision5) Up(op Operation) error { func (r *revision5) Up(mg *MigrationDriver) error {
_, err := op.Exec(`CREATE INDEX samples_url_name_ix ON samples (url, name)`) _, err := mg.Tx.Exec(`CREATE INDEX samples_url_name_ix ON samples (url, name)`)
return err return err
} }
func (r *revision5) Down(op Operation) error { func (r *revision5) Down(mg *MigrationDriver) error {
_, err := op.Exec(`DROP INDEX samples_url_name_ix`) _, err := mg.Tx.Exec(`DROP INDEX samples_url_name_ix`)
return err return err
} }
@ -143,15 +143,15 @@ func (r *revision5) Revision() int64 {
// ---------- revision 6 // ---------- revision 6
type revision6 struct{} type revision6 struct{}
func (r *revision6) Up(op Operation) error { func (r *revision6) Up(mg *MigrationDriver) error {
_, err := op.RenameColumns("samples", map[string]string{ _, err := mg.RenameColumns("samples", map[string]string{
"url": "host", "url": "host",
}) })
return err return err
} }
func (r *revision6) Down(op Operation) error { func (r *revision6) Down(mg *MigrationDriver) error {
_, err := op.RenameColumns("samples", map[string]string{ _, err := mg.RenameColumns("samples", map[string]string{
"host": "url", "host": "url",
}) })
return err return err
@ -166,16 +166,16 @@ func (r *revision6) Revision() int64 {
// ---------- revision 7 // ---------- revision 7
type revision7 struct{} type revision7 struct{}
func (r *revision7) Up(op Operation) error { func (r *revision7) Up(mg *MigrationDriver) error {
_, err := op.DropColumns("samples", []string{"host", "num"}) _, err := mg.DropColumns("samples", []string{"host", "num"})
return err return err
} }
func (r *revision7) Down(op Operation) error { func (r *revision7) Down(mg *MigrationDriver) error {
if _, err := op.AddColumn("samples", "host VARCHAR(255)"); err != nil { if _, err := mg.AddColumn("samples", "host VARCHAR(255)"); err != nil {
return err return err
} }
_, err := op.AddColumn("samples", "num INSTEGER") _, err := mg.AddColumn("samples", "num INSTEGER")
return err return err
} }
@ -188,16 +188,16 @@ func (r *revision7) Revision() int64 {
// ---------- revision 8 // ---------- revision 8
type revision8 struct{} type revision8 struct{}
func (r *revision8) Up(op Operation) error { func (r *revision8) Up(mg *MigrationDriver) error {
if _, err := op.AddColumn("samples", "repo_id INTEGER"); err != nil { if _, err := mg.AddColumn("samples", "repo_id INTEGER"); err != nil {
return err return err
} }
_, err := op.AddColumn("samples", "repo VARCHAR(255)") _, err := mg.AddColumn("samples", "repo VARCHAR(255)")
return err return err
} }
func (r *revision8) Down(op Operation) error { func (r *revision8) Down(mg *MigrationDriver) error {
_, err := op.DropColumns("samples", []string{"repo", "repo_id"}) _, err := mg.DropColumns("samples", []string{"repo", "repo_id"})
return err return err
} }
@ -210,15 +210,15 @@ func (r *revision8) Revision() int64 {
// ---------- revision 9 // ---------- revision 9
type revision9 struct{} type revision9 struct{}
func (r *revision9) Up(op Operation) error { func (r *revision9) Up(mg *MigrationDriver) error {
_, err := op.RenameColumns("samples", map[string]string{ _, err := mg.RenameColumns("samples", map[string]string{
"repo": "repository", "repo": "repository",
}) })
return err return err
} }
func (r *revision9) Down(op Operation) error { func (r *revision9) Down(mg *MigrationDriver) error {
_, err := op.RenameColumns("samples", map[string]string{ _, err := mg.RenameColumns("samples", map[string]string{
"repository": "repo", "repository": "repo",
}) })
return err return err