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/GeertJohan/go.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/russross/meddler

View file

@ -1,7 +1,6 @@
package main
import (
"database/sql"
"flag"
"log"
"net/http"
@ -12,13 +11,10 @@ import (
"code.google.com/p/go.net/websocket"
"github.com/GeertJohan/go.rice"
"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/channel"
"github.com/drone/drone/pkg/database"
"github.com/drone/drone/pkg/database/migrate"
"github.com/drone/drone/pkg/handler"
"github.com/drone/drone/pkg/queue"
)
@ -66,7 +62,9 @@ func main() {
checkTLSFlags()
// setup database and handlers
setupDatabase()
if err := database.Init(driver, datasource); err != nil {
log.Fatal("Can't initialize database:", err)
}
setupStatic()
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
// be directly embedded inside the application using
// the `rice embed` command, else they are served from disk.

View file

@ -4,12 +4,53 @@ import (
"database/sql"
"log"
"github.com/drone/drone/pkg/database/migrate"
"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.
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.
func Set(database *sql.DB) {
// set the global database

View file

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

View file

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

View file

@ -64,22 +64,17 @@ type Operation interface {
DropColumns(tableName string, columnsToDrop []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 {
Up(op Operation) error
Down(op Operation) error
Up(mg *MigrationDriver) error
Down(mg *MigrationDriver) error
Revision() int64
}
type MigrationDriver struct {
Tx *sql.Tx
Operation
}
type Migration struct {
@ -87,7 +82,9 @@ type Migration struct {
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 {
return &Migration{db: db}
@ -148,14 +145,14 @@ func (m *Migration) up(target, current int64) error {
return err
}
op := Driver(tx)
mg := Driver(tx)
// loop through and execute revisions
for _, rev := range m.revs {
if rev.Revision() > current && rev.Revision() <= target {
current = rev.Revision()
// 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.Println(err)
return tx.Rollback()
@ -181,7 +178,7 @@ func (m *Migration) down(target, current int64) error {
return err
}
op := Driver(tx)
mg := Driver(tx)
// reverse the list of revisions
revs := []Revision{}
@ -195,7 +192,7 @@ func (m *Migration) down(target, current int64) error {
if rev.Revision() > target {
current = rev.Revision()
// 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.Println(err)
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"
)
type SQLiteDriver MigrationDriver
func SQLite(tx *sql.Tx) Operation {
return &SQLiteDriver{Tx: tx}
type sqliteDriver struct {
Tx *sql.Tx
}
func (s *SQLiteDriver) Exec(query string, args ...interface{}) (sql.Result, error) {
return s.Tx.Exec(query, args...)
func SQLite(tx *sql.Tx) *MigrationDriver {
return &MigrationDriver{
Tx: tx,
Operation: &sqliteDriver{Tx: tx},
}
}
func (s *SQLiteDriver) Query(query string, args ...interface{}) (*sql.Rows, 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) {
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, ", ")))
}
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))
}
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))
}
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))
}
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 result sql.Result
@ -144,7 +137,7 @@ func (s *SQLiteDriver) DropColumns(tableName string, columnsToDrop []string) (sq
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 result sql.Result
@ -243,7 +236,7 @@ func (s *SQLiteDriver) RenameColumns(tableName string, columnChanges map[string]
return result, err
}
func (s *SQLiteDriver) getDDLFromTable(tableName string) (string, error) {
func (s *sqliteDriver) getDDLFromTable(tableName string) (string, error) {
var sql string
query := `SELECT sql FROM sqlite_master WHERE type='table' and name=?;`
err := s.Tx.QueryRow(query, tableName).Scan(&sql)
@ -253,7 +246,7 @@ func (s *SQLiteDriver) getDDLFromTable(tableName string) (string, error) {
return sql, nil
}
func (s *SQLiteDriver) getDDLFromIndex(tableName string) ([]string, error) {
func (s *sqliteDriver) getDDLFromIndex(tableName string) ([]string, error) {
var sqls []string
query := `SELECT sql FROM sqlite_master WHERE type='index' and tbl_name=?;`

View file

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