Skip to content

Commit 7a0fb45

Browse files
committed
feat(be): boltDB migration
1 parent 04066c6 commit 7a0fb45

9 files changed

Lines changed: 657 additions & 166 deletions

File tree

cli/cmd/migrate.go

Lines changed: 77 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
11
package cmd
22

33
import (
4+
"errors"
5+
"fmt"
6+
"os"
7+
8+
"github.com/semaphoreui/semaphore/db/factory"
9+
"github.com/semaphoreui/semaphore/db_migration"
410
"github.com/semaphoreui/semaphore/util"
511
"github.com/spf13/cobra"
612
)
713

814
var migrationArgs struct {
9-
undoTo string
10-
applyTo string
15+
undoTo string
16+
applyTo string
17+
fromBoltDb string
1118
}
1219

1320
func init() {
1421
migrateCmd.PersistentFlags().StringVar(&migrationArgs.undoTo, "undo-to", "", "Undo to specific version")
1522
migrateCmd.PersistentFlags().StringVar(&migrationArgs.applyTo, "apply-to", "", "Apply to specific version")
16-
23+
migrateCmd.PersistentFlags().StringVar(&migrationArgs.fromBoltDb, "from-boltdb", "", "Path to boltDB data file")
1724
rootCmd.AddCommand(migrateCmd)
1825
}
1926

@@ -22,23 +29,80 @@ var migrateCmd = &cobra.Command{
2229
Short: "Execute migrations",
2330
Run: func(cmd *cobra.Command, args []string) {
2431

25-
var undoTo, applyTo *string
26-
2732
if migrationArgs.undoTo != "" && migrationArgs.applyTo != "" {
2833
panic("Cannot specify both --undo-to and --apply-to")
34+
} else if migrationArgs.undoTo != "" || migrationArgs.applyTo != "" {
35+
var undoTo, applyTo *string
36+
37+
if migrationArgs.undoTo != "" {
38+
undoTo = &migrationArgs.undoTo
39+
}
40+
41+
if migrationArgs.applyTo != "" {
42+
applyTo = &migrationArgs.applyTo
43+
}
44+
45+
store := createStoreWithMigrationVersion("migrate", undoTo, applyTo)
46+
47+
defer store.Close("migrate")
48+
util.Config.PrintDbInfo()
2949
}
3050

31-
if migrationArgs.undoTo != "" {
32-
undoTo = &migrationArgs.undoTo
51+
if migrationArgs.fromBoltDb != "" {
52+
migrateBoltDb(migrationArgs.fromBoltDb)
3353
}
54+
},
55+
}
3456

35-
if migrationArgs.applyTo != "" {
36-
applyTo = &migrationArgs.applyTo
57+
func migrateBoltDb(boltDbPath string) {
58+
59+
boltCfg := util.DbConfig{
60+
Dialect: util.DbDriverBolt,
61+
Hostname: boltDbPath,
62+
}
63+
64+
if boltCfg.Dialect != util.DbDriverBolt {
65+
fmt.Printf("Error: Source database must be BoltDB (dialect: %s)\n", boltCfg.Dialect)
66+
return
67+
}
68+
69+
_, err := os.Stat(boltDbPath)
70+
if err != nil {
71+
if errors.Is(err, os.ErrNotExist) {
72+
fmt.Println("File does not exist")
73+
} else {
74+
fmt.Printf("Error: %v\n", err)
3775
}
76+
return
77+
}
3878

39-
store := createStoreWithMigrationVersion("migrate", undoTo, applyTo)
79+
boltStore := factory.CreateStoreWithConfig(boltCfg)
80+
boltStore.Connect("")
4081

41-
defer store.Close("migrate")
42-
util.Config.PrintDbInfo()
43-
},
82+
// 2. Create SQL Store
83+
cfg, _ := util.ConfigInitNew("", true, true)
84+
sqlCfg, err := cfg.GetDBConfig()
85+
86+
if err != nil {
87+
fmt.Printf("Error reading SQL DB config: %v\n", err)
88+
return
89+
}
90+
91+
if sqlCfg.Dialect == util.DbDriverBolt {
92+
fmt.Println("Error: Destination database must be a SQL database")
93+
return
94+
}
95+
96+
sqlStore := factory.CreateStoreWithConfig(sqlCfg)
97+
sqlStore.Connect("")
98+
99+
// 3. Connect and migrate
100+
fmt.Println("Starting migration...")
101+
err = db_migration.Migrate(boltStore, sqlStore)
102+
if err != nil {
103+
fmt.Printf("Migration failed: %v\n", err)
104+
return
105+
}
106+
107+
fmt.Println("Migration finished successfully.")
44108
}

db/Store.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ type UserManager interface {
233233
CreateUser(user UserWithPwd) (User, error)
234234
DeleteUser(userID int) error
235235
UpdateUser(user UserWithPwd) error
236+
ImportUser(user UserWithPwd) (User, error)
236237
SetUserPassword(userID int, password string) error
237238
AddTotpVerification(userID int, url string, recoveryHash string) (UserTotp, error)
238239
DeleteTotpVerification(userID int, totpID int) error

db/bolt/BoltDb.go

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ func (d emptyEnumerable) Next() (key []byte, value []byte) {
3636
}
3737

3838
type BoltDb struct {
39+
config util.DbConfig
40+
3941
Filename string
4042
db *bbolt.DB
4143
connections map[string]bool
@@ -55,8 +57,8 @@ var terraformAliasProps = db.ObjectProps{
5557
PrimaryColumnName: "alias",
5658
}
5759

58-
func CreateBoltDB() *BoltDb {
59-
res := BoltDb{}
60+
func CreateBoltDBWithConfig(config util.DbConfig) *BoltDb {
61+
res := BoltDb{config: config}
6062
res.integrationAlias = publicAlias{
6163
aliasProps: db.IntegrationAliasProps,
6264
publicAliasProps: integrationAliasProps,
@@ -70,6 +72,14 @@ func CreateBoltDB() *BoltDb {
7072
return &res
7173
}
7274

75+
func CreateBoltDB() *BoltDb {
76+
conf, err := util.Config.GetDBConfig()
77+
if err != nil {
78+
panic(err)
79+
}
80+
return CreateBoltDBWithConfig(conf)
81+
}
82+
7383
type objectID interface {
7484
ToBytes() []byte
7585
}
@@ -102,11 +112,7 @@ func makeBucketId(props db.ObjectProps, ids ...int) []byte {
102112
func (d *BoltDb) openDbFile() {
103113
var filename string
104114
if d.Filename == "" {
105-
config, err := util.Config.GetDBConfig()
106-
if err != nil {
107-
panic(err)
108-
}
109-
filename = config.GetHostname()
115+
filename = d.config.Hostname
110116
} else {
111117
filename = d.Filename
112118
}
@@ -188,12 +194,7 @@ func (d *BoltDb) Close(token string) {
188194
}
189195

190196
func (d *BoltDb) PermanentConnection() bool {
191-
config, err := util.Config.GetDBConfig()
192-
if err != nil {
193-
panic(err)
194-
}
195-
196-
isSessionConnection, ok := config.Options["sessionConnection"]
197+
isSessionConnection, ok := d.config.Options["sessionConnection"]
197198

198199
if ok && (isSessionConnection == "true" || isSessionConnection == "yes") {
199200
return false

db/bolt/user.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package bolt
22

33
import (
4+
"errors"
45
"fmt"
6+
57
"github.com/semaphoreui/semaphore/db"
68
"github.com/semaphoreui/semaphore/pkg/tz"
79
"golang.org/x/crypto/bcrypt"
@@ -38,6 +40,10 @@ func (d *BoltDb) CreateUserWithoutPassword(user db.User) (newUser db.User, err e
3840
return
3941
}
4042

43+
func (d *BoltDb) ImportUser(user db.UserWithPwd) (newUser db.User, err error) {
44+
return db.User{}, errors.New("unsupported operation")
45+
}
46+
4147
func (d *BoltDb) CreateUser(user db.UserWithPwd) (newUser db.User, err error) {
4248

4349
err = db.ValidateUser(user.User)

db/factory/store.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,20 @@ func CreateStore() db.Store {
1212
if err != nil {
1313
panic("Can not read configuration")
1414
}
15+
return CreateStoreWithConfig(config)
16+
}
17+
18+
func CreateStoreWithConfig(config util.DbConfig) db.Store {
19+
1520
switch config.Dialect {
16-
case util.DbDriverMySQL:
17-
return sql.CreateDb(config.Dialect)
1821
case util.DbDriverBolt:
19-
return bolt.CreateBoltDB()
22+
return bolt.CreateBoltDBWithConfig(config)
23+
24+
case util.DbDriverMySQL:
2025
case util.DbDriverPostgres:
21-
return sql.CreateDb(config.Dialect)
2226
case util.DbDriverSQLite:
23-
return sql.CreateDb(config.Dialect)
27+
return sql.CreateDbWithConfig(config)
28+
2429
default:
2530
panic("Unsupported database dialect: " + config.Dialect)
2631
}

db/sql/SqlDb.go

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,16 @@ type SqlDbConnection struct {
2828
}
2929

3030
type SqlDb struct {
31+
config util.DbConfig
3132
connection SqlDbConnection
3233
}
3334

3435
func (d *SqlDb) Sql() *gorp.DbMap {
3536
return d.connection.sql
3637
}
3738

38-
func (d *SqlDbConnection) Connect() {
39-
sqlDb, err := connect()
39+
func (d *SqlDbConnection) Connect(config util.DbConfig) {
40+
sqlDb, err := connect(config)
4041
if err != nil {
4142
panic(err)
4243
}
@@ -47,11 +48,11 @@ func (d *SqlDbConnection) Connect() {
4748
log.Warn("Cannot close database connection: " + err.Error())
4849
}
4950

50-
if err = createDb(); err != nil {
51+
if err = createDb(config); err != nil {
5152
panic(err)
5253
}
5354

54-
sqlDb, err = connect()
55+
sqlDb, err = connect(config)
5556
if err != nil {
5657
panic(err)
5758
}
@@ -61,14 +62,9 @@ func (d *SqlDbConnection) Connect() {
6162
}
6263
}
6364

64-
cfg, err := util.Config.GetDBConfig()
65-
if err != nil {
66-
panic(err)
67-
}
68-
6965
var dialect gorp.Dialect
7066

71-
switch cfg.Dialect {
67+
switch config.Dialect {
7268
case util.DbDriverMySQL:
7369
dialect = gorp.MySQLDialect{Engine: "InnoDB", Encoding: "UTF8"}
7470
case util.DbDriverPostgres:
@@ -324,14 +320,24 @@ create table ` + "`migrations`" + ` (
324320
//go:embed migrations/*.sql
325321
var dbAssets embed.FS
326322

327-
func CreateDb(dialect string) *SqlDb {
323+
func CreateDbWithConfig(config util.DbConfig) *SqlDb {
328324
return &SqlDb{
325+
config: config,
329326
connection: SqlDbConnection{
330-
dialect: dialect,
327+
dialect: config.Dialect,
331328
},
332329
}
333330
}
334331

332+
func CreateDb(dialect string) *SqlDb {
333+
conf, err := util.Config.GetDBConfig()
334+
335+
if err != nil {
336+
panic(err)
337+
}
338+
return CreateDbWithConfig(conf)
339+
}
340+
335341
func (d *SqlDbConnection) GetDialect() string {
336342
return d.dialect
337343
}
@@ -436,12 +442,7 @@ func (d *SqlDb) selectAll(i any, query string, args ...any) ([]any, error) {
436442
return d.connection.SelectAll(i, query, args...)
437443
}
438444

439-
func connect() (*sql.DB, error) {
440-
cfg, err := util.Config.GetDBConfig()
441-
if err != nil {
442-
return nil, err
443-
}
444-
445+
func connect(cfg util.DbConfig) (*sql.DB, error) {
445446
connectionString, err := cfg.GetConnectionString(true)
446447
if err != nil {
447448
return nil, err
@@ -451,12 +452,7 @@ func connect() (*sql.DB, error) {
451452
return sql.Open(dialect, connectionString)
452453
}
453454

454-
func createDb() error {
455-
cfg, err := util.Config.GetDBConfig()
456-
if err != nil {
457-
return err
458-
}
459-
455+
func createDb(cfg util.DbConfig) error {
460456
if !cfg.HasSupportMultipleDatabases() {
461457
return nil
462458
}
@@ -473,7 +469,7 @@ func createDb() error {
473469

474470
defer conn.Close() //nolint:errcheck
475471

476-
_, err = conn.Exec("create database " + cfg.GetDbName())
472+
_, err = conn.Exec("create database " + cfg.DbName)
477473
if err != nil {
478474
log.Warn(err.Error())
479475
}
@@ -562,7 +558,7 @@ func (d *SqlDb) PermanentConnection() bool {
562558
}
563559

564560
func (d *SqlDb) Connect(_ string) {
565-
d.connection.Connect()
561+
d.connection.Connect(d.config)
566562
}
567563

568564
func (d *SqlDb) getObjectRefs(projectID int, objectProps db.ObjectProps, objectID int) (refs db.ObjectReferrers, err error) {

db/sql/user.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,24 @@ func (d *SqlDb) CreateUser(user db.UserWithPwd) (newUser db.User, err error) {
5555
return
5656
}
5757

58+
func (d *SqlDb) ImportUser(user db.UserWithPwd) (newUser db.User, err error) {
59+
err = db.ValidateUser(user.User)
60+
if err != nil {
61+
return
62+
}
63+
64+
user.Created = db.GetParsedTime(tz.Now())
65+
66+
err = d.Sql().Insert(&user.User)
67+
68+
if err != nil {
69+
return
70+
}
71+
72+
newUser = user.User
73+
return
74+
}
75+
5876
func (d *SqlDb) DeleteUser(userID int) error {
5977
res, err := d.exec("delete from `user` where id=?", userID)
6078
return validateMutationResult(res, err)

0 commit comments

Comments
 (0)