diff options
author | wxiaoguang <wxiaoguang@gmail.com> | 2021-12-01 15:50:01 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-12-01 15:50:01 +0800 |
commit | 042cac5fedeec8af53080b9666fe043072f3a6be (patch) | |
tree | b13d57faa71ba8bc9f8b3d40f5be7e3735ac66a4 /models | |
parent | a3517d8668482b58cb80ba10a956fe4e27e1a429 (diff) | |
download | gitea-042cac5fedeec8af53080b9666fe043072f3a6be.tar.gz gitea-042cac5fedeec8af53080b9666fe043072f3a6be.zip |
Improve install code to avoid low-level mistakes. (#17779)
* Improve install code to avoid low-level mistakes.
If a user tries to do a re-install in a Gitea database, they gets a warning and double check.
When Gitea runs, it never create empty app.ini automatically.
Also some small (related) refactoring:
* Refactor db.InitEngine related logic make it more clean (especially for the install code)
* Move some i18n strings out from setting.go to make the setting.go can be easily maintained.
* Show errors in CLI code if an incorrect app.ini is used.
* APP_DATA_PATH is created when installing, and checked when starting (no empty directory is created any more).
Diffstat (limited to 'models')
-rwxr-xr-x | models/db/engine.go | 68 | ||||
-rw-r--r-- | models/db/install/db.go | 65 | ||||
-rw-r--r-- | models/migrations/migrations_test.go | 23 | ||||
-rw-r--r-- | models/ssh_key_test.go | 2 | ||||
-rw-r--r-- | models/unittest/fixtures.go | 7 | ||||
-rw-r--r-- | models/unittest/testdb.go | 3 |
6 files changed, 109 insertions, 59 deletions
diff --git a/models/db/engine.go b/models/db/engine.go index 0f744d027e..63e1d5547a 100755 --- a/models/db/engine.go +++ b/models/db/engine.go @@ -8,7 +8,6 @@ package db import ( "context" "database/sql" - "errors" "fmt" "io" "reflect" @@ -92,8 +91,8 @@ func init() { } } -// NewEngine returns a new xorm engine from the configuration -func NewEngine() (*xorm.Engine, error) { +// newXORMEngine returns a new XORM engine from the configuration +func newXORMEngine() (*xorm.Engine, error) { connStr, err := setting.DBConnStr() if err != nil { return nil, err @@ -126,40 +125,49 @@ func SyncAllTables() error { return x.StoreEngine("InnoDB").Sync2(tables...) } -// InitEngine sets the xorm.Engine -func InitEngine(ctx context.Context) (err error) { - x, err = NewEngine() +// InitEngine initializes the xorm.Engine and sets it as db.DefaultContext +func InitEngine(ctx context.Context) error { + xormEngine, err := newXORMEngine() if err != nil { - return fmt.Errorf("Failed to connect to database: %v", err) + return fmt.Errorf("failed to connect to database: %v", err) } - x.SetMapper(names.GonicMapper{}) + xormEngine.SetMapper(names.GonicMapper{}) // WARNING: for serv command, MUST remove the output to os.stdout, // so use log file to instead print to stdout. - x.SetLogger(NewXORMLogger(setting.Database.LogSQL)) - x.ShowSQL(setting.Database.LogSQL) - x.SetMaxOpenConns(setting.Database.MaxOpenConns) - x.SetMaxIdleConns(setting.Database.MaxIdleConns) - x.SetConnMaxLifetime(setting.Database.ConnMaxLifetime) + xormEngine.SetLogger(NewXORMLogger(setting.Database.LogSQL)) + xormEngine.ShowSQL(setting.Database.LogSQL) + xormEngine.SetMaxOpenConns(setting.Database.MaxOpenConns) + xormEngine.SetMaxIdleConns(setting.Database.MaxIdleConns) + xormEngine.SetConnMaxLifetime(setting.Database.ConnMaxLifetime) + xormEngine.SetDefaultContext(ctx) + + SetDefaultEngine(ctx, xormEngine) + return nil +} +// SetDefaultEngine sets the default engine for db +func SetDefaultEngine(ctx context.Context, eng *xorm.Engine) { + x = eng DefaultContext = &Context{ Context: ctx, e: x, } - x.SetDefaultContext(ctx) - return nil } -// SetEngine is used by unit test code -func SetEngine(eng *xorm.Engine) { - x = eng - DefaultContext = &Context{ - Context: context.Background(), - e: x, +// UnsetDefaultEngine closes and unsets the default engine +// We hope the SetDefaultEngine and UnsetDefaultEngine can be paired, but it's impossible now, +// there are many calls to InitEngine -> SetDefaultEngine directly to overwrite the `x` and DefaultContext without close +// Global database engine related functions are all racy and there is no graceful close right now. +func UnsetDefaultEngine() { + if x != nil { + _ = x.Close() + x = nil } + DefaultContext = nil } -// InitEngineWithMigration initializes a new xorm.Engine +// InitEngineWithMigration initializes a new xorm.Engine and sets it as the db.DefaultContext // This function must never call .Sync2() if the provided migration function fails. // When called from the "doctor" command, the migration function is a version check // that prevents the doctor from fixing anything in the database if the migration level @@ -226,14 +234,6 @@ func NamesToBean(names ...string) ([]interface{}, error) { return beans, nil } -// Ping tests if database is alive -func Ping() error { - if x != nil { - return x.Ping() - } - return errors.New("database not configured") -} - // DumpDatabase dumps all data from database according the special database SQL syntax to file system. func DumpDatabase(filePath, dbType string) error { var tbs []*schemas.Table @@ -291,11 +291,3 @@ func GetMaxID(beanOrTableName interface{}) (maxID int64, err error) { _, err = x.Select("MAX(id)").Table(beanOrTableName).Get(&maxID) return } - -// FindByMaxID filled results as the condition from database -func FindByMaxID(maxID int64, limit int, results interface{}) error { - return x.Where("id <= ?", maxID). - OrderBy("id DESC"). - Limit(limit). - Find(results) -} diff --git a/models/db/install/db.go b/models/db/install/db.go new file mode 100644 index 0000000000..363a8c8679 --- /dev/null +++ b/models/db/install/db.go @@ -0,0 +1,65 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package install + +import ( + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/modules/setting" + + "xorm.io/xorm" +) + +func getXORMEngine() *xorm.Engine { + return db.DefaultContext.(*db.Context).Engine().(*xorm.Engine) +} + +// CheckDatabaseConnection checks the database connection +func CheckDatabaseConnection() error { + e := db.GetEngine(db.DefaultContext) + _, err := e.Exec("SELECT 1") + return err +} + +// GetMigrationVersion gets the database migration version +func GetMigrationVersion() (int64, error) { + var installedDbVersion int64 + x := getXORMEngine() + exist, err := x.IsTableExist("version") + if err != nil { + return 0, err + } + if !exist { + return 0, nil + } + _, err = x.Table("version").Cols("version").Get(&installedDbVersion) + if err != nil { + return 0, err + } + return installedDbVersion, nil +} + +// HasPostInstallationUsers checks whether there are users after installation +func HasPostInstallationUsers() (bool, error) { + x := getXORMEngine() + exist, err := x.IsTableExist("user") + if err != nil { + return false, err + } + if !exist { + return false, nil + } + + // if there are 2 or more users in database, we consider there are users created after installation + threshold := 2 + if !setting.IsProd { + // to debug easily, with non-prod RUN_MODE, we only check the count to 1 + threshold = 1 + } + res, err := x.Table("user").Cols("id").Limit(threshold).Query() + if err != nil { + return false, err + } + return len(res) >= threshold, nil +} diff --git a/models/migrations/migrations_test.go b/models/migrations/migrations_test.go index f46070cf8e..2effcb99eb 100644 --- a/models/migrations/migrations_test.go +++ b/models/migrations/migrations_test.go @@ -5,6 +5,7 @@ package migrations import ( + "context" "database/sql" "fmt" "os" @@ -57,7 +58,7 @@ func TestMain(m *testing.M) { } setting.SetCustomPathAndConf("", "", "") - setting.NewContext() + setting.LoadForTest() git.CheckLFSVersion() setting.InitDBConfig() setting.NewLogServices(true) @@ -85,21 +86,11 @@ func removeAllWithRetry(dir string) error { return err } -// newEngine sets the xorm.Engine -func newEngine() (*xorm.Engine, error) { - x, err := db.NewEngine() - if err != nil { - return x, fmt.Errorf("Failed to connect to database: %v", err) +func newXORMEngine() (*xorm.Engine, error) { + if err := db.InitEngine(context.Background()); err != nil { + return nil, err } - - x.SetMapper(names.GonicMapper{}) - // WARNING: for serv command, MUST remove the output to os.stdout, - // so use log file to instead print to stdout. - x.SetLogger(db.NewXORMLogger(setting.Database.LogSQL)) - x.ShowSQL(setting.Database.LogSQL) - x.SetMaxOpenConns(setting.Database.MaxOpenConns) - x.SetMaxIdleConns(setting.Database.MaxIdleConns) - x.SetConnMaxLifetime(setting.Database.ConnMaxLifetime) + x := unittest.GetXORMEngine() return x, nil } @@ -213,7 +204,7 @@ func prepareTestEnv(t *testing.T, skip int, syncModels ...interface{}) (*xorm.En return nil, deferFn } - x, err := newEngine() + x, err := newXORMEngine() assert.NoError(t, err) if x != nil { oldDefer := deferFn diff --git a/models/ssh_key_test.go b/models/ssh_key_test.go index cc78f2604b..b52a36bdbd 100644 --- a/models/ssh_key_test.go +++ b/models/ssh_key_test.go @@ -16,7 +16,7 @@ import ( func init() { setting.SetCustomPathAndConf("", "", "") - setting.NewContext() + setting.LoadForTest() } func Test_SSHParsePublicKey(t *testing.T) { diff --git a/models/unittest/fixtures.go b/models/unittest/fixtures.go index 6277d1c725..e39acd766f 100644 --- a/models/unittest/fixtures.go +++ b/models/unittest/fixtures.go @@ -18,7 +18,8 @@ import ( var fixtures *testfixtures.Loader -func getXORMEngine(engine ...*xorm.Engine) (x *xorm.Engine) { +// GetXORMEngine gets the XORM engine +func GetXORMEngine(engine ...*xorm.Engine) (x *xorm.Engine) { if len(engine) == 1 { return engine[0] } @@ -27,7 +28,7 @@ func getXORMEngine(engine ...*xorm.Engine) (x *xorm.Engine) { // InitFixtures initialize test fixtures for a test database func InitFixtures(opts FixturesOptions, engine ...*xorm.Engine) (err error) { - e := getXORMEngine(engine...) + e := GetXORMEngine(engine...) var testfiles func(*testfixtures.Loader) error if opts.Dir != "" { testfiles = testfixtures.Directory(opts.Dir) @@ -69,7 +70,7 @@ func InitFixtures(opts FixturesOptions, engine ...*xorm.Engine) (err error) { // LoadFixtures load fixtures for a test database func LoadFixtures(engine ...*xorm.Engine) error { - e := getXORMEngine(engine...) + e := GetXORMEngine(engine...) var err error // Database transaction conflicts could occur and result in ROLLBACK // As a simple workaround, we just retry 20 times. diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go index 8771bc1d21..94c93755e0 100644 --- a/models/unittest/testdb.go +++ b/models/unittest/testdb.go @@ -5,6 +5,7 @@ package unittest import ( + "context" "fmt" "net/url" "os" @@ -124,7 +125,7 @@ func CreateTestEngine(opts FixturesOptions) error { return err } x.SetMapper(names.GonicMapper{}) - db.SetEngine(x) + db.SetDefaultEngine(context.Background(), x) if err = db.SyncAllTables(); err != nil { return err |