diff --git a/docker/blocks/mysql/fig b/docker/blocks/mysql/fig index 5adc10a8c10..eb13cac31c8 100644 --- a/docker/blocks/mysql/fig +++ b/docker/blocks/mysql/fig @@ -1,7 +1,9 @@ -db: +mysql: image: mysql:latest environment: MYSQL_ROOT_PASSWORD: rootpass MYSQL_DATABASE: grafana MYSQL_USER: grafana MYSQL_PASSWORD: password + ports: + - "3306:3306" diff --git a/docker/blocks/mysql_tests/fig b/docker/blocks/mysql_tests/fig new file mode 100644 index 00000000000..880c955d218 --- /dev/null +++ b/docker/blocks/mysql_tests/fig @@ -0,0 +1,9 @@ +mysqltests: + image: mysql:latest + environment: + MYSQL_ROOT_PASSWORD: rootpass + MYSQL_DATABASE: grafana_tests + MYSQL_USER: grafana + MYSQL_PASSWORD: password + ports: + - "3306:3306" diff --git a/docker/fig.yml b/docker/fig.yml index e7b675cfd3b..efaed13c4c9 100644 --- a/docker/fig.yml +++ b/docker/fig.yml @@ -1,10 +1,10 @@ -openldap: - image: cnry/openldap +mysqltests: + image: mysql:latest environment: - SLAPD_PASSWORD: grafana - SLAPD_DOMAIN: grafana.org + MYSQL_ROOT_PASSWORD: rootpass + MYSQL_DATABASE: grafana_tests + MYSQL_USER: grafana + MYSQL_PASSWORD: password ports: - - "389:389" - - + - "3306:3306" diff --git a/pkg/api/account.go b/pkg/api/account.go index 94dfa693465..3085415deb7 100644 --- a/pkg/api/account.go +++ b/pkg/api/account.go @@ -8,9 +8,8 @@ import ( func GetAccount(c *middleware.Context) { query := m.GetAccountInfoQuery{Id: c.AccountId} - err := bus.Dispatch(&query) - if err != nil { + if err := bus.Dispatch(&query); err != nil { c.JsonApiErr(500, "Failed to fetch collaboratos", err) return } @@ -31,9 +30,8 @@ func UpdateAccount(c *middleware.Context, cmd m.UpdateAccountCommand) { func GetOtherAccounts(c *middleware.Context) { query := m.GetOtherAccountsQuery{AccountId: c.AccountId} - err := bus.Dispatch(&query) - if err != nil { + if err := bus.Dispatch(&query); err != nil { c.JsonApiErr(500, "Failed to get other accounts", err) return } diff --git a/pkg/services/sqlstore/migrations/builder.go b/pkg/services/sqlstore/migrations/builder.go index 7fa9412f314..0d5ba6856dd 100644 --- a/pkg/services/sqlstore/migrations/builder.go +++ b/pkg/services/sqlstore/migrations/builder.go @@ -3,9 +3,27 @@ package migrations type migration struct { desc string sqlite string + mysql string verifyTable string } +type columnType string + +const ( + DB_TYPE_STRING columnType = "String" +) + +func (m *migration) getSql(dbType string) string { + switch dbType { + case "mysql": + return m.mysql + case "sqlite3": + return m.sqlite + } + + panic("db type not supported") +} + type migrationBuilder struct { migration *migration } @@ -15,6 +33,11 @@ func (b *migrationBuilder) sqlite(sql string) *migrationBuilder { return b } +func (b *migrationBuilder) mysql(sql string) *migrationBuilder { + b.migration.mysql = sql + return b +} + func (b *migrationBuilder) verifyTable(name string) *migrationBuilder { b.migration.verifyTable = name return b diff --git a/pkg/services/sqlstore/migrations/engine.go b/pkg/services/sqlstore/migrations/engine.go index aeb75ad6c0e..9260bab8259 100644 --- a/pkg/services/sqlstore/migrations/engine.go +++ b/pkg/services/sqlstore/migrations/engine.go @@ -4,8 +4,12 @@ import ( "errors" "fmt" - "github.com/go-xorm/xorm" "github.com/torkelo/grafana-pro/pkg/services/sqlstore/sqlsyntax" + + _ "github.com/go-sql-driver/mysql" + "github.com/go-xorm/xorm" + _ "github.com/lib/pq" + _ "github.com/mattn/go-sqlite3" ) var x *xorm.Engine @@ -29,9 +33,18 @@ func getSchemaVersion() (int, error) { return v.Version, err } -func StartMigration(engine *xorm.Engine) error { +func setEngineAndDialect(engine *xorm.Engine) { x = engine - dialect = new(sqlsyntax.Sqlite3) + switch x.DriverName() { + case "mysql": + dialect = new(sqlsyntax.Mysql) + case "sqlite3": + dialect = new(sqlsyntax.Sqlite3) + } +} + +func StartMigration(engine *xorm.Engine) error { + setEngineAndDialect(engine) _, err := getSchemaVersion() if err != nil { @@ -49,16 +62,21 @@ func StartMigration(engine *xorm.Engine) error { func execMigration(m *migration) error { err := inTransaction(func(sess *xorm.Session) error { - _, err := sess.Exec(m.sqlite) + _, err := sess.Exec(m.getSql(x.DriverName())) if err != nil { return err } return nil }) + if err != nil { return err } - // verify + + return verifyMigration(m) +} + +func verifyMigration(m *migration) error { if m.verifyTable != "" { sqlStr, args := dialect.TableCheckSql(m.verifyTable) results, err := x.Query(sqlStr, args...) @@ -66,7 +84,6 @@ func execMigration(m *migration) error { return errors.New(fmt.Sprintf("Verify failed: table %v does not exist", m.verifyTable)) } } - return nil } diff --git a/pkg/services/sqlstore/migrations/migrations.go b/pkg/services/sqlstore/migrations/migrations.go index 71964f079ee..94c42ad6bc4 100644 --- a/pkg/services/sqlstore/migrations/migrations.go +++ b/pkg/services/sqlstore/migrations/migrations.go @@ -4,13 +4,24 @@ var migrationList []*migration func init() { new(migrationBuilder). + // ------------------------------ desc("Create account table"). sqlite(` CREATE TABLE account ( - id INTEGER PRIMARY KEY + id INTEGER PRIMARY KEY AUTOINCREMENT ) `). - verifyTable("account") + mysql(` + CREATE TABLE account ( + id BIGINT NOT NULL AUTO_INCREMENT, PRIMARY KEY (id) + ) + `). + verifyTable("account").add() + // ------------------------------ + // desc("Add name column to account table"). + // table("account").addColumn("name").colType(DB_TYPE_STRING) + // sqlite("ALTER TABLE account ADD COLUMN name TEXT"). + // mysql("ALTER TABLE account ADD COLUMN name NVARCHAR(255)"). } type SchemaVersion struct { diff --git a/pkg/services/sqlstore/migrations/migrations_test.go b/pkg/services/sqlstore/migrations/migrations_test.go index c670f856592..d30d360bca9 100644 --- a/pkg/services/sqlstore/migrations/migrations_test.go +++ b/pkg/services/sqlstore/migrations/migrations_test.go @@ -1,22 +1,55 @@ package migrations -// import ( -// "testing" -// -// "github.com/go-xorm/xorm" -// -// . "github.com/smartystreets/goconvey/convey" -// ) -// -// func TestMigrationsSqlite(t *testing.T) { -// -// Convey("Initial SQLite3 migration", t, func() { -// x, err := xorm.NewEngine("sqlite3", ":memory:") -// StartMigration(x) -// -// tables, err := x.DBMetas() -// So(err, ShouldBeNil) -// -// So(len(tables), ShouldEqual, 1) -// }) -// } +import ( + "fmt" + "testing" + + "github.com/go-xorm/xorm" + + . "github.com/smartystreets/goconvey/convey" +) + +func cleanDB(x *xorm.Engine) { + tables, _ := x.DBMetas() + sess := x.NewSession() + defer sess.Close() + + for _, table := range tables { + if _, err := sess.Exec("SET FOREIGN_KEY_CHECKS = 0"); err != nil { + panic("Failed to disable foreign key checks") + } + if _, err := sess.Exec("DROP TABLE " + table.Name); err != nil { + panic(fmt.Sprintf("Failed to delete table: %v, err: %v", table.Name, err)) + } + if _, err := sess.Exec("SET FOREIGN_KEY_CHECKS = 1"); err != nil { + panic("Failed to disable foreign key checks") + } + } +} + +func TestMigrationsSqlite(t *testing.T) { + testDBs := [][]string{ + []string{"sqlite3", ":memory:"}, + []string{"mysql", "grafana:password@tcp(localhost:3306)/grafana_tests?charset=utf8"}, + } + + for _, testDB := range testDBs { + + Convey("Initial "+testDB[0]+" migration", t, func() { + x, err := xorm.NewEngine(testDB[0], testDB[1]) + So(err, ShouldBeNil) + + if testDB[0] == "mysql" { + cleanDB(x) + } + + StartMigration(x) + + tables, err := x.DBMetas() + So(err, ShouldBeNil) + + So(len(tables), ShouldEqual, 2) + }) + + } +} diff --git a/pkg/services/sqlstore/sqlsyntax/dialect.go b/pkg/services/sqlstore/sqlsyntax/dialect.go index 526ce2decaf..6104f885c86 100644 --- a/pkg/services/sqlstore/sqlsyntax/dialect.go +++ b/pkg/services/sqlstore/sqlsyntax/dialect.go @@ -8,11 +8,24 @@ type Dialect interface { type Sqlite3 struct { } +type Mysql struct { +} + func (db *Sqlite3) DBType() string { return "sqlite3" } +func (db *Mysql) DBType() string { + return "mysql" +} + func (db *Sqlite3) TableCheckSql(tableName string) (string, []interface{}) { args := []interface{}{tableName} return "SELECT name FROM sqlite_master WHERE type='table' and name = ?", args } + +func (db *Mysql) TableCheckSql(tableName string) (string, []interface{}) { + args := []interface{}{"grafana", tableName} + sql := "SELECT `TABLE_NAME` from `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`=? and `TABLE_NAME`=?" + return sql, args +}