migrator.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. package migrations
  2. import (
  3. "time"
  4. _ "github.com/go-sql-driver/mysql"
  5. "github.com/go-xorm/xorm"
  6. _ "github.com/lib/pq"
  7. _ "github.com/mattn/go-sqlite3"
  8. "github.com/torkelo/grafana-pro/pkg/log"
  9. )
  10. type Migrator struct {
  11. x *xorm.Engine
  12. dialect Dialect
  13. migrations []Migration
  14. }
  15. func NewMigrator(engine *xorm.Engine) *Migrator {
  16. mg := &Migrator{}
  17. mg.x = engine
  18. mg.migrations = make([]Migration, 0)
  19. switch mg.x.DriverName() {
  20. case MYSQL:
  21. mg.dialect = new(Mysql)
  22. case SQLITE:
  23. mg.dialect = new(Sqlite3)
  24. }
  25. return mg
  26. }
  27. func (mg *Migrator) AddMigration(id string, m Migration) {
  28. m.SetId(id)
  29. mg.migrations = append(mg.migrations, m)
  30. }
  31. func (mg *Migrator) GetMigrationLog() (map[string]MigrationLog, error) {
  32. exists, err := mg.x.IsTableExist(new(MigrationLog))
  33. if err != nil {
  34. return nil, err
  35. }
  36. if !exists {
  37. if err := mg.x.CreateTables(new(MigrationLog)); err != nil {
  38. return nil, err
  39. }
  40. return nil, nil
  41. }
  42. logMap := make(map[string]MigrationLog)
  43. logItems := make([]MigrationLog, 0)
  44. if err = mg.x.Find(&logItems); err != nil {
  45. return nil, err
  46. }
  47. for _, logItem := range logItems {
  48. if !logItem.Success {
  49. continue
  50. }
  51. logMap[logItem.MigrationId] = logItem
  52. }
  53. return logMap, nil
  54. }
  55. func (mg *Migrator) Start() error {
  56. log.Info("Migrator::Start DB migration")
  57. logMap, err := mg.GetMigrationLog()
  58. if err != nil {
  59. return err
  60. }
  61. for _, m := range mg.migrations {
  62. _, exists := logMap[m.Id()]
  63. if exists {
  64. log.Info("Migrator:: Skipping migration: %v, Already executed", m.Id())
  65. continue
  66. }
  67. record := MigrationLog{
  68. MigrationId: m.Id(),
  69. Sql: m.Sql(mg.dialect),
  70. Timestamp: time.Now(),
  71. }
  72. if err := mg.exec(m); err != nil {
  73. record.Error = err.Error()
  74. mg.x.Insert(&record)
  75. return err
  76. } else {
  77. record.Success = true
  78. mg.x.Insert(&record)
  79. }
  80. }
  81. return nil
  82. }
  83. func (mg *Migrator) exec(m Migration) error {
  84. log.Info("Migrator::exec migration id: %v", m.Id())
  85. err := mg.inTransaction(func(sess *xorm.Session) error {
  86. _, err := sess.Exec(m.Sql(mg.dialect))
  87. if err != nil {
  88. log.Error(3, "Migrator::exec FAILED migration id: %v, err: %v", m.Id(), err)
  89. return err
  90. }
  91. return nil
  92. })
  93. if err != nil {
  94. return err
  95. }
  96. return nil
  97. }
  98. type dbTransactionFunc func(sess *xorm.Session) error
  99. func (mg *Migrator) inTransaction(callback dbTransactionFunc) error {
  100. var err error
  101. sess := mg.x.NewSession()
  102. defer sess.Close()
  103. if err = sess.Begin(); err != nil {
  104. return err
  105. }
  106. err = callback(sess)
  107. if err != nil {
  108. sess.Rollback()
  109. return err
  110. } else if err = sess.Commit(); err != nil {
  111. return err
  112. }
  113. return nil
  114. }