-
Notifications
You must be signed in to change notification settings - Fork 1.1k
cubemaster: fix integration tests panicking on unopened dao #1383
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,6 +24,7 @@ import ( | |
| "github.com/tencentcloud/CubeSandbox/CubeDB/migrate" | ||
| "github.com/tencentcloud/CubeSandbox/CubeDB/tombstone" | ||
| "github.com/tencentcloud/CubeSandbox/CubeMaster/pkg/base/config" | ||
| "github.com/tencentcloud/CubeSandbox/CubeMaster/pkg/base/db" | ||
| "github.com/tencentcloud/CubeSandbox/CubeMaster/pkg/base/log" | ||
| "github.com/tencentcloud/CubeSandbox/CubeMaster/pkg/base/recov" | ||
| "github.com/tencentcloud/CubeSandbox/CubeMaster/pkg/cubelet/grpcconn" | ||
|
|
@@ -235,23 +236,13 @@ func initDatabaseSchema(ctx context.Context, cfg *config.Config) error { | |
| // covering the host/node inventory tables (t_cube_host_*, t_cube_node_*) | ||
| // and the instance tables (t_cube_template_*, t_cube_instance_*, | ||
| // t_cube_sandbox_spec, ...), all in the one configured database. | ||
| src := cfg.InstanceDBConfig | ||
| if src == nil { | ||
| return fmt.Errorf("dao: instance_db_config is not set") | ||
| } | ||
| daoCfg := dao.Config{ | ||
| Driver: src.Driver, | ||
| Addr: src.Addr, | ||
| User: src.User, | ||
| Pwd: src.Pwd, | ||
| DBName: src.DBName, | ||
| ConnTimeoutSeconds: src.ConnTimeout, | ||
| ReadTimeoutSeconds: src.ReadTimeout, | ||
| WriteTimeoutSeconds: src.WriteTimeout, | ||
| MaxIdleConns: src.MaxIdleConns, | ||
| MaxOpenConns: src.MaxOpenConns, | ||
| MaxConnLifeTimeSeconds: src.MaxConnLifeTimeSeconds, | ||
| MigrationLockTimeoutSeconds: src.MigrationLockTimeoutSeconds, | ||
| // Build the dao config from the same snapshot as the integration / | ||
| // mock-debug bootstrap: config hotswap could otherwise change the | ||
| // identity between the two dao.Open calls and the second one would | ||
| // fail with "dao: already opened with ... (requested ...)". | ||
| daoCfg, err := db.ConfigFromDBConfig(cfg.InstanceDBConfig) | ||
| if err != nil { | ||
| return fmt.Errorf("dao: %w", err) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Non-blocking: this refactor drops the actionable error text for a missing DB config. The old path reported No caller matches on the old string, and in the app this path is unreachable with a nil config because There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor diagnostic regression: the removed |
||
| } | ||
| if _, err := dao.Open(ctx, daoCfg); err != nil { | ||
| return fmt.Errorf("dao open: %w", err) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,6 +22,7 @@ import ( | |
| "github.com/alicebob/miniredis/v2" | ||
| "github.com/gomodule/redigo/redis" | ||
| "github.com/google/uuid" | ||
| "github.com/tencentcloud/CubeSandbox/CubeDB/dao" | ||
| "github.com/tencentcloud/CubeSandbox/CubeMaster/api/services/cubebox/v1" | ||
| cubeleterrorcode "github.com/tencentcloud/CubeSandbox/CubeMaster/api/services/errorcode/v1" | ||
| "github.com/tencentcloud/CubeSandbox/CubeMaster/api/services/images/v1" | ||
|
|
@@ -654,10 +655,26 @@ func metricNow() []byte { | |
| } | ||
|
|
||
| func mock_db() { | ||
| mocktest_OssDb = db.Init(config.GetDbConfig()) | ||
| // db.Init is a thin wrapper that returns dao.Default(), which panics | ||
| // with "dao: Open has not been called yet" until the shared handle is | ||
| // established by dao.Open. Open it here from the loaded DB config; | ||
| // dao.Open is idempotent for the same identity, so the later | ||
| // dao.Open in app.Run's startup path (initDatabaseSchema) is a no-op. | ||
| // Schema (including t_cube_host_type) is owned by the dao.Migrate | ||
| // path that the integration test bootstrap runs before tests. | ||
| // path that the app bootstrap runs before tests. The dao config must | ||
| // be built from the same snapshot as initDatabaseSchema: config | ||
| // hotswap between MockInit and app.Run could change the identity and | ||
| // make the second dao.Open fail with "dao: already opened with ...". | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the contribution! The overall behavioral change looks good to me. However, I think we can clean up the comments here a bit. We should focus on describing what the code does, rather than explaining the rationale behind this panic. Otherwise, it might confuse future readers and comes across as noise.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for review! Already changed. |
||
| daoCfg, err := db.ConfigFromDBConfig(config.GetDbConfig()) | ||
| if err != nil { | ||
| stdlog.Fatalf("integration: dao config fail: %v", err) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Non-blocking robustness note: this fix rests on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| } | ||
| if _, err := dao.Open(mocktest_Ctx, daoCfg); err != nil { | ||
| stdlog.Fatalf("dao open fail:%v", err) | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The fix is correct for the two known bootstrap paths, but it leaves |
||
| mocktest_OssDb = db.Init(config.GetDbConfig()) | ||
| } | ||
|
|
||
| func mock_getstr() string { | ||
| return fmt.Sprintf("%d.%d.%d.%d", rand.Int31n(254), rand.Int31n(254), rand.Int31n(254), rand.Int31n(254)) | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,8 @@ | |
| package db | ||
|
|
||
| import ( | ||
| "errors" | ||
|
|
||
| "github.com/tencentcloud/CubeSandbox/CubeDB/dao" | ||
| "github.com/tencentcloud/CubeSandbox/CubeMaster/pkg/base/config" | ||
| "gorm.io/gorm" | ||
|
|
@@ -20,3 +22,28 @@ func Init(cfg *config.DBConfig) *gorm.DB { | |
| _ = cfg | ||
| return dao.Default() | ||
| } | ||
|
|
||
| // ConfigFromDBConfig maps a config.DBConfig to the dao.Config used to open | ||
| // the shared database handle. dao.Open keys its idempotence on this config | ||
| // identity, so every call site that opens the handle before app startup | ||
| // (schema init, integration/mock-debug bootstrap) must build the dao config | ||
| // through this helper — keeping the two mappings from drifting. | ||
| func ConfigFromDBConfig(src *config.DBConfig) (dao.Config, error) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Trivial: the helper is generic over There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This helper is a second copy of the field list that |
||
| if src == nil { | ||
| return dao.Config{}, errors.New("db config is nil: instance_db_config is not set") | ||
| } | ||
| return dao.Config{ | ||
| Driver: src.Driver, | ||
| Addr: src.Addr, | ||
| User: src.User, | ||
| Pwd: src.Pwd, | ||
| DBName: src.DBName, | ||
| ConnTimeoutSeconds: src.ConnTimeout, | ||
| ReadTimeoutSeconds: src.ReadTimeout, | ||
| WriteTimeoutSeconds: src.WriteTimeout, | ||
| MaxIdleConns: src.MaxIdleConns, | ||
| MaxOpenConns: src.MaxOpenConns, | ||
| MaxConnLifeTimeSeconds: src.MaxConnLifeTimeSeconds, | ||
| MigrationLockTimeoutSeconds: src.MigrationLockTimeoutSeconds, | ||
| }, nil | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor precision nit on the comment: the two call sites don't actually read the same snapshot.
mock_db()reads the live global viaconfig.GetDbConfig(), while this readscfg.InstanceDBConfigfrom thecfgcaptured atRun()start. WhatConfigFromDBConfigguarantees is the same mapping, not the same snapshot — if a config hot-reload landed in the window betweenMockInit()andapp.Run(), the two identities would still differ and this seconddao.Openwould fail withalready opened with ... (requested ...). The comment inmock_init.goalready acknowledges that window honestly; consider rewording to "same mapping as" so the claim here doesn't overstate the protection.