Skip to content

Commit 44d2003

Browse files
kanard38knard38
authored andcommitted
DAOS-18304 ddb: Add unit test infrastructure to ddb Go code
Introduce a DdbApi Go interface to decouple the DDB command layer from the C VOS implementation, enabling Go unit testing with stubs that do not require a live VOS environment. Refactoring: - Introduce DdbApi interface; convert all ddbXxx() free functions to (ctx *DdbContext) Xxx() methods; propagate DdbApi through addAppCommands(), createGrumbleApp() and parseOpts(). - Convert Init() to a method on a caller-allocated DdbContext. - Remove dc_pool_path and dc_db_path from struct ddb_ctx: these were workarounds for passing path strings from Go to C via the context struct, replaced by explicit function arguments at every call site. Remove the SetCString() helper accordingly. - Replace prefixesToSkip+HasPrefix loop with a noAutoOpen map for exact command-name matching. - Remove os.Exit() from parseOpts() and printHelp()/printCmdHelp(): all errors now flow up to main() which is the single call site for exitWithError(), making parseOpts() fully testable. Bug fixes: - ddb_run_feature() (C): guard dc_poh/dc_write_mode reset with the 'close' flag to prevent state corruption when the pool was already open; add missing error message on open failure. - Non-interactive mode: return errors from runCmdStr() and runFileCmds() instead of silently discarding them. - Move debug.SetTraceback("crash") to main() so it runs before any other code including DisableCStdoutBuffering(). - --version: print directly instead of synthesising a fake command. - --db_path without --vos_path: return an explicit error. - Pool close: use a closePoolIfOpen() helper with defer on all paths. - Feature() Go: fix memory leak on enable/disable C strings. - DtxStat() Go: fix defer ordering for freeString(options.path). Invalid command error messages: - Introduce a typed unknownCmdError struct for type-safe detection. - exitWithError() prints the command list on unknownCmdError and fixes the duplicated ERROR: prefix on fault resolutions. - printCmdHelp(): unknown command + --help now returns an error to the caller instead of calling exitWithError() directly, consistent with the single-exit-point design. rm_pool command: - Make --vos_path mandatory. - Pass db_path explicitly: RmPool() -> ddb_run_rm_pool() -> dv_pool_destroy(). feature command: - Pass db_path explicitly: Feature() -> ddb_run_feature() -> dv_pool_open(). The interactive open command is also updated. VOS path length: - Raise DB_PATH_SIZE and VOS_PATH_SIZE from 256 to PATH_MAX. - Store the original VOS path in vf_vos_file_path and validate its length in parse_vos_file_parts(). dv_pool_open / dv_pool_destroy API: - Simplify to (path, db_path, ctx, flags); move vos_file_parts allocation into a new internal create_vos_file_parts() helper. Test fixes: - Move DTX record insertion to dcv_suit_setup() for a shared fixture. - Correct VOS tree path indices in ls, value_dump and ilog_dump tests. - Update dtx_stat regex to match new "DTX entries statistics of the pool:" message, removing the stale dc_pool_path null reference. Features: recovery Signed-off-by: Cedric Koch-Hofer <cedric.koch-hofer@hpe.com>
1 parent bec98cf commit 44d2003

13 files changed

Lines changed: 399 additions & 326 deletions

src/control/cmd/ddb/commands_wrapper.go

Lines changed: 98 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -37,46 +37,67 @@ func freeString(s *C.char) {
3737
C.free(unsafe.Pointer(s))
3838
}
3939

40-
func SetCString(out **C.char, s string) func() {
41-
cstr := C.CString(s)
42-
*out = cstr
40+
type DdbAPI interface {
41+
Init(log *logging.LeveledLogger) (func(), error)
42+
PoolIsOpen() bool
43+
Ls(path string, recursive bool, details bool) error
44+
Open(path string, dbPath string, writeMode bool) error
45+
Version() error
46+
Close() error
47+
SuperblockDump() error
48+
ValueDump(path string, dst string) error
49+
Rm(path string) error
50+
ValueLoad(src string, dst string) error
51+
IlogDump(path string) error
52+
IlogCommit(path string) error
53+
IlogClear(path string) error
54+
DtxDump(path string, active bool, committed bool) error
55+
DtxCmtClear(path string) error
56+
SmdSync(nvmeConf string, dbPath string) error
57+
VeaDump() error
58+
VeaUpdate(offset string, blkCnt string) error
59+
DtxActCommit(path string, dtxID string) error
60+
DtxActAbort(path string, dtxID string) error
61+
Feature(path, dbPath, enable, disable string, show bool) error
62+
RmPool(path string, dbPath string) error
63+
DtxActDiscardInvalid(path string, dtxID string) error
64+
DevList(dbPath string) error
65+
DevReplace(dbPath string, oldDevid string, newDevid string) error
66+
DtxStat(path string, details bool) error
67+
ProvMem(dbPath string, tmpfsMount string, tmpfsMountSize uint) error
68+
DtxAggr(path string, cmtTime uint64, cmtDate string) error
69+
}
4370

44-
return func() {
45-
C.free(unsafe.Pointer(cstr))
46-
}
71+
// DdbContext structure for wrapping the C code context structure
72+
type DdbContext struct {
73+
ctx C.struct_ddb_ctx
74+
log *logging.LeveledLogger
4775
}
4876

49-
// InitDdb initializes the ddb context and returns a closure to finalize it.
50-
func InitDdb(log *logging.LeveledLogger) (*DdbContext, func(), error) {
77+
// Init initializes the ddb context and returns a closure to finalize it.
78+
func (ctx *DdbContext) Init(log *logging.LeveledLogger) (func(), error) {
5179
// Must lock to OS thread because vos init/fini uses ABT init and finalize which must be called on the same thread
5280
runtime.LockOSThread()
5381

5482
if err := daosError(C.ddb_init()); err != nil {
5583
runtime.UnlockOSThread()
56-
return nil, nil, err
84+
return nil, err
5785
}
5886

59-
ctx := &DdbContext{}
6087
C.ddb_ctx_init(&ctx.ctx) // Initialize with ctx default values
6188
ctx.log = log
6289

63-
return ctx, func() {
90+
return func() {
6491
C.ddb_fini()
6592
runtime.UnlockOSThread()
6693
}, nil
6794
}
6895

69-
// DdbContext structure for wrapping the C code context structure
70-
type DdbContext struct {
71-
ctx C.struct_ddb_ctx
72-
log *logging.LeveledLogger
73-
}
74-
75-
func ddbPoolIsOpen(ctx *DdbContext) bool {
96+
func (ctx *DdbContext) PoolIsOpen() bool {
7697
return bool(C.ddb_pool_is_open(&ctx.ctx))
7798
}
7899

79-
func ddbLs(ctx *DdbContext, path string, recursive bool, details bool) error {
100+
func (ctx *DdbContext) Ls(path string, recursive bool, details bool) error {
80101
/* Set up the options */
81102
options := C.struct_ls_options{}
82103
options.path = C.CString(path)
@@ -87,33 +108,34 @@ func ddbLs(ctx *DdbContext, path string, recursive bool, details bool) error {
87108
return daosError(C.ddb_run_ls(&ctx.ctx, &options))
88109
}
89110

90-
func ddbOpen(ctx *DdbContext, path string, write_mode bool) error {
111+
func (ctx *DdbContext) Open(path string, dbPath string, writeMode bool) error {
91112
/* Set up the options */
92113
options := C.struct_open_options{}
93114
options.path = C.CString(path)
94115
defer freeString(options.path)
95-
options.db_path = ctx.ctx.dc_db_path
96-
options.write_mode = C.bool(write_mode)
116+
options.db_path = C.CString(dbPath)
117+
defer freeString(options.db_path)
118+
options.write_mode = C.bool(writeMode)
97119
/* Run the c code command */
98120
return daosError(C.ddb_run_open(&ctx.ctx, &options))
99121
}
100122

101-
func ddbVersion(ctx *DdbContext) error {
123+
func (ctx *DdbContext) Version() error {
102124
/* Run the c code command */
103125
return daosError(C.ddb_run_version(&ctx.ctx))
104126
}
105127

106-
func ddbClose(ctx *DdbContext) error {
128+
func (ctx *DdbContext) Close() error {
107129
/* Run the c code command */
108130
return daosError(C.ddb_run_close(&ctx.ctx))
109131
}
110132

111-
func ddbSuperblockDump(ctx *DdbContext) error {
133+
func (ctx *DdbContext) SuperblockDump() error {
112134
/* Run the c code command */
113135
return daosError(C.ddb_run_superblock_dump(&ctx.ctx))
114136
}
115137

116-
func ddbValueDump(ctx *DdbContext, path string, dst string) error {
138+
func (ctx *DdbContext) ValueDump(path string, dst string) error {
117139
/* Set up the options */
118140
options := C.struct_value_dump_options{}
119141
options.path = C.CString(path)
@@ -124,7 +146,7 @@ func ddbValueDump(ctx *DdbContext, path string, dst string) error {
124146
return daosError(C.ddb_run_value_dump(&ctx.ctx, &options))
125147
}
126148

127-
func ddbRm(ctx *DdbContext, path string) error {
149+
func (ctx *DdbContext) Rm(path string) error {
128150
/* Set up the options */
129151
options := C.struct_rm_options{}
130152
options.path = C.CString(path)
@@ -133,7 +155,7 @@ func ddbRm(ctx *DdbContext, path string) error {
133155
return daosError(C.ddb_run_rm(&ctx.ctx, &options))
134156
}
135157

136-
func ddbValueLoad(ctx *DdbContext, src string, dst string) error {
158+
func (ctx *DdbContext) ValueLoad(src string, dst string) error {
137159
/* Set up the options */
138160
options := C.struct_value_load_options{}
139161
options.src = C.CString(src)
@@ -144,7 +166,7 @@ func ddbValueLoad(ctx *DdbContext, src string, dst string) error {
144166
return daosError(C.ddb_run_value_load(&ctx.ctx, &options))
145167
}
146168

147-
func ddbIlogDump(ctx *DdbContext, path string) error {
169+
func (ctx *DdbContext) IlogDump(path string) error {
148170
/* Set up the options */
149171
options := C.struct_ilog_dump_options{}
150172
options.path = C.CString(path)
@@ -153,7 +175,7 @@ func ddbIlogDump(ctx *DdbContext, path string) error {
153175
return daosError(C.ddb_run_ilog_dump(&ctx.ctx, &options))
154176
}
155177

156-
func ddbIlogCommit(ctx *DdbContext, path string) error {
178+
func (ctx *DdbContext) IlogCommit(path string) error {
157179
/* Set up the options */
158180
options := C.struct_ilog_commit_options{}
159181
options.path = C.CString(path)
@@ -162,7 +184,7 @@ func ddbIlogCommit(ctx *DdbContext, path string) error {
162184
return daosError(C.ddb_run_ilog_commit(&ctx.ctx, &options))
163185
}
164186

165-
func ddbIlogClear(ctx *DdbContext, path string) error {
187+
func (ctx *DdbContext) IlogClear(path string) error {
166188
/* Set up the options */
167189
options := C.struct_ilog_clear_options{}
168190
options.path = C.CString(path)
@@ -171,7 +193,7 @@ func ddbIlogClear(ctx *DdbContext, path string) error {
171193
return daosError(C.ddb_run_ilog_clear(&ctx.ctx, &options))
172194
}
173195

174-
func ddbDtxDump(ctx *DdbContext, path string, active bool, committed bool) error {
196+
func (ctx *DdbContext) DtxDump(path string, active bool, committed bool) error {
175197
/* Set up the options */
176198
options := C.struct_dtx_dump_options{}
177199
options.path = C.CString(path)
@@ -182,7 +204,7 @@ func ddbDtxDump(ctx *DdbContext, path string, active bool, committed bool) error
182204
return daosError(C.ddb_run_dtx_dump(&ctx.ctx, &options))
183205
}
184206

185-
func ddbDtxCmtClear(ctx *DdbContext, path string) error {
207+
func (ctx *DdbContext) DtxCmtClear(path string) error {
186208
/* Set up the options */
187209
options := C.struct_dtx_cmt_clear_options{}
188210
options.path = C.CString(path)
@@ -191,70 +213,75 @@ func ddbDtxCmtClear(ctx *DdbContext, path string) error {
191213
return daosError(C.ddb_run_dtx_cmt_clear(&ctx.ctx, &options))
192214
}
193215

194-
func ddbSmdSync(ctx *DdbContext, nvme_conf string, db_path string) error {
216+
func (ctx *DdbContext) SmdSync(nvmeConf string, dbPath string) error {
195217
/* Set up the options */
196218
options := C.struct_smd_sync_options{}
197-
options.nvme_conf = C.CString(nvme_conf)
219+
options.nvme_conf = C.CString(nvmeConf)
198220
defer freeString(options.nvme_conf)
199-
options.db_path = C.CString(db_path)
221+
options.db_path = C.CString(dbPath)
200222
defer freeString(options.db_path)
201223
/* Run the c code command */
202224
return daosError(C.ddb_run_smd_sync(&ctx.ctx, &options))
203225
}
204226

205-
func ddbVeaDump(ctx *DdbContext) error {
227+
func (ctx *DdbContext) VeaDump() error {
206228
/* Run the c code command */
207229
return daosError(C.ddb_run_vea_dump(&ctx.ctx))
208230
}
209231

210-
func ddbVeaUpdate(ctx *DdbContext, offset string, blk_cnt string) error {
232+
func (ctx *DdbContext) VeaUpdate(offset string, blkCnt string) error {
211233
/* Set up the options */
212234
options := C.struct_vea_update_options{}
213235
options.offset = C.CString(offset)
214236
defer freeString(options.offset)
215-
options.blk_cnt = C.CString(blk_cnt)
237+
options.blk_cnt = C.CString(blkCnt)
216238
defer freeString(options.blk_cnt)
217239
/* Run the c code command */
218240
return daosError(C.ddb_run_vea_update(&ctx.ctx, &options))
219241
}
220242

221-
func ddbDtxActCommit(ctx *DdbContext, path string, dtx_id string) error {
243+
func (ctx *DdbContext) DtxActCommit(path string, dtxID string) error {
222244
/* Set up the options */
223245
options := C.struct_dtx_act_options{}
224246
options.path = C.CString(path)
225247
defer freeString(options.path)
226-
options.dtx_id = C.CString(dtx_id)
248+
options.dtx_id = C.CString(dtxID)
227249
defer freeString(options.dtx_id)
228250
/* Run the c code command */
229251
return daosError(C.ddb_run_dtx_act_commit(&ctx.ctx, &options))
230252
}
231253

232-
func ddbDtxActAbort(ctx *DdbContext, path string, dtx_id string) error {
254+
func (ctx *DdbContext) DtxActAbort(path string, dtxID string) error {
233255
/* Set up the options */
234256
options := C.struct_dtx_act_options{}
235257
options.path = C.CString(path)
236258
defer freeString(options.path)
237-
options.dtx_id = C.CString(dtx_id)
259+
options.dtx_id = C.CString(dtxID)
238260
defer freeString(options.dtx_id)
239261
/* Run the c code command */
240262
return daosError(C.ddb_run_dtx_act_abort(&ctx.ctx, &options))
241263
}
242264

243-
func ddbFeature(ctx *DdbContext, path, enable, disable string, show bool) error {
265+
func (ctx *DdbContext) Feature(path, dbPath, enable, disable string, show bool) error {
244266
/* Set up the options */
245267
options := C.struct_feature_options{}
246268
options.path = C.CString(path)
247269
defer freeString(options.path)
248-
options.db_path = ctx.ctx.dc_db_path
270+
options.db_path = C.CString(dbPath)
271+
defer freeString(options.db_path)
249272
if enable != "" {
250-
err := daosError(C.ddb_feature_string2flags(&ctx.ctx, C.CString(enable),
273+
cEnable := C.CString(enable)
274+
defer freeString(cEnable)
275+
err := daosError(C.ddb_feature_string2flags(&ctx.ctx, cEnable,
251276
&options.set_compat_flags, &options.set_incompat_flags))
252277
if err != nil {
253278
return err
254279
}
255280
}
256281
if disable != "" {
257-
err := daosError(C.ddb_feature_string2flags(&ctx.ctx, C.CString(disable),
282+
cDisable := C.CString(disable)
283+
defer freeString(cDisable)
284+
err := daosError(C.ddb_feature_string2flags(&ctx.ctx, cDisable,
258285
&options.clear_compat_flags, &options.clear_incompat_flags))
259286
if err != nil {
260287
return err
@@ -265,78 +292,79 @@ func ddbFeature(ctx *DdbContext, path, enable, disable string, show bool) error
265292
return daosError(C.ddb_run_feature(&ctx.ctx, &options))
266293
}
267294

268-
func ddbRmPool(ctx *DdbContext, path string) error {
295+
func (ctx *DdbContext) RmPool(path string, dbPath string) error {
269296
/* Set up the options */
270297
options := C.struct_rm_pool_options{}
271298
options.path = C.CString(path)
272299
defer freeString(options.path)
273-
options.db_path = ctx.ctx.dc_db_path
300+
options.db_path = C.CString(dbPath)
301+
defer freeString(options.db_path)
274302
/* Run the c code command */
275303
return daosError(C.ddb_run_rm_pool(&ctx.ctx, &options))
276304
}
277305

278-
func ddbDtxActDiscardInvalid(ctx *DdbContext, path string, dtx_id string) error {
306+
func (ctx *DdbContext) DtxActDiscardInvalid(path string, dtxID string) error {
279307
/* Set up the options */
280308
options := C.struct_dtx_act_options{}
281309
options.path = C.CString(path)
282310
defer freeString(options.path)
283-
options.dtx_id = C.CString(dtx_id)
311+
options.dtx_id = C.CString(dtxID)
284312
defer freeString(options.dtx_id)
285313
/* Run the c code command */
286314
return daosError(C.ddb_run_dtx_act_discard_invalid(&ctx.ctx, &options))
287315
}
288316

289-
func ddbDevList(ctx *DdbContext, db_path string) error {
317+
func (ctx *DdbContext) DevList(dbPath string) error {
290318
/* Set up the options */
291319
options := C.struct_dev_list_options{}
292-
options.db_path = C.CString(db_path)
320+
options.db_path = C.CString(dbPath)
293321
defer freeString(options.db_path)
294322
/* Run the c code command */
295323
return daosError(C.ddb_run_dev_list(&ctx.ctx, &options))
296324
}
297325

298-
func ddbDevReplace(ctx *DdbContext, db_path string, old_devid string, new_devid string) error {
326+
func (ctx *DdbContext) DevReplace(dbPath string, oldDevID string, newDevID string) error {
299327
/* Set up the options */
300328
options := C.struct_dev_replace_options{}
301-
options.db_path = C.CString(db_path)
329+
options.db_path = C.CString(dbPath)
302330
defer freeString(options.db_path)
303-
options.old_devid = C.CString(old_devid)
331+
options.old_devid = C.CString(oldDevID)
304332
defer freeString(options.old_devid)
305-
options.new_devid = C.CString(new_devid)
333+
options.new_devid = C.CString(newDevID)
306334
defer freeString(options.new_devid)
307335
/* Run the c code command */
308336
return daosError(C.ddb_run_dev_replace(&ctx.ctx, &options))
309337
}
310338

311-
func ddbDtxStat(ctx *DdbContext, path string, details bool) error {
339+
func (ctx *DdbContext) DtxStat(path string, details bool) error {
312340
/* Set up the options */
313341
options := C.struct_dtx_stat_options{}
314342
options.path = C.CString(path)
315-
options.details = C.bool(details)
316343
defer freeString(options.path)
344+
options.details = C.bool(details)
317345
/* Run the c code command */
318346
return daosError(C.ddb_run_dtx_stat(&ctx.ctx, &options))
319347
}
320348

321-
func ddbProvMem(ctx *DdbContext, db_path string, tmpfs_mount string, tmpfs_mount_size uint) error {
349+
func (ctx *DdbContext) ProvMem(dbPath string, tmpfsMount string, tmpfsMountSize uint) error {
322350
/* Set up the options */
323351
options := C.struct_prov_mem_options{}
324-
options.db_path = C.CString(db_path)
352+
options.db_path = C.CString(dbPath)
325353
defer freeString(options.db_path)
326-
options.tmpfs_mount = C.CString(tmpfs_mount)
354+
options.tmpfs_mount = C.CString(tmpfsMount)
327355
defer freeString(options.tmpfs_mount)
328356

329-
options.tmpfs_mount_size = C.uint(tmpfs_mount_size)
357+
options.tmpfs_mount_size = C.uint(tmpfsMountSize)
330358
/* Run the c code command */
331359
return daosError(C.ddb_run_prov_mem(&ctx.ctx, &options))
332360
}
333361

334-
func ddbDtxAggr(ctx *DdbContext, path string, cmt_time uint64, cmt_date string) error {
335-
if cmt_time != math.MaxUint64 && cmt_date != "" {
362+
func (ctx *DdbContext) DtxAggr(path string, cmtTime uint64, cmtDate string) error {
363+
if cmtTime != math.MaxUint64 && cmtDate != "" {
336364
ctx.log.Error("'--cmt_time' and '--cmt_date' options are mutually exclusive")
337365
return daosError(-C.DER_INVAL)
338366
}
339-
if cmt_time == math.MaxUint64 && cmt_date == "" {
367+
if cmtTime == math.MaxUint64 && cmtDate == "" {
340368
ctx.log.Error("'--cmt_time' or '--cmt_date' option has to be defined")
341369
return daosError(-C.DER_INVAL)
342370
}
@@ -345,13 +373,13 @@ func ddbDtxAggr(ctx *DdbContext, path string, cmt_time uint64, cmt_date string)
345373
options := C.struct_dtx_aggr_options{}
346374
options.path = C.CString(path)
347375
defer freeString(options.path)
348-
if cmt_time != math.MaxUint64 {
376+
if cmtTime != math.MaxUint64 {
349377
options.format = C.DDB_DTX_AGGR_CMT_TIME
350-
options.cmt_time = C.uint64_t(cmt_time)
378+
options.cmt_time = C.uint64_t(cmtTime)
351379
}
352-
if cmt_date != "" {
380+
if cmtDate != "" {
353381
options.format = C.DDB_DTX_AGGR_CMT_DATE
354-
options.cmt_date = C.CString(cmt_date)
382+
options.cmt_date = C.CString(cmtDate)
355383
defer freeString(options.cmt_date)
356384
}
357385
/* Run the c code command */

0 commit comments

Comments
 (0)