Skip to content

Commit bb2329c

Browse files
authored
DAOS-17693 control: Correctly copy mkfs options (#17498)
Options were not being copied properly into the mkfs argument slice. This PR fixes the issue and improves unit testing. Signed-off-by: Kris Jacque <kris.jacque@hpe.com>
1 parent 895120a commit bb2329c

File tree

4 files changed

+152
-54
lines changed

4 files changed

+152
-54
lines changed

src/control/provider/system/system_linux.go

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//
22
// (C) Copyright 2019-2024 Intel Corporation.
3-
// (C) Copyright 2025 Hewlett Packard Enterprise Development LP
3+
// (C) Copyright 2025-2026 Hewlett Packard Enterprise Development LP
44
//
55
// SPDX-License-Identifier: BSD-2-Clause-Patent
66
//
@@ -56,12 +56,18 @@ var magicToStr = map[int64]string{
5656

5757
// DefaultProvider returns the package-default provider implementation.
5858
func DefaultProvider() *LinuxProvider {
59-
return &LinuxProvider{}
59+
return &LinuxProvider{
60+
runCommand: func(name string, args ...string) ([]byte, error) {
61+
return exec.Command(name, args...).Output()
62+
},
63+
}
6064
}
6165

6266
// LinuxProvider encapsulates Linux-specific implementations of system
6367
// interfaces.
64-
type LinuxProvider struct{}
68+
type LinuxProvider struct {
69+
runCommand func(string, ...string) ([]byte, error)
70+
}
6571

6672
// mountId,parentId,major:minor,root,mountPoint
6773
const (
@@ -254,6 +260,10 @@ type MkfsReq struct {
254260
// Mkfs attempts to create a filesystem of the supplied type, on the
255261
// supplied device.
256262
func (s LinuxProvider) Mkfs(req MkfsReq) error {
263+
if req.Filesystem == "" {
264+
return errors.New("no filesystem type specified")
265+
}
266+
257267
cmdPath, err := exec.LookPath(fmt.Sprintf("mkfs.%s", req.Filesystem))
258268
if err != nil {
259269
return errors.Wrapf(err, "unable to find mkfs.%s", req.Filesystem)
@@ -263,7 +273,7 @@ func (s LinuxProvider) Mkfs(req MkfsReq) error {
263273
return err
264274
}
265275

266-
args := make([]string, 0, len(req.Options))
276+
args := make([]string, len(req.Options))
267277
_ = copy(args, req.Options)
268278
// TODO: Think about a way to allow for some kind of progress
269279
// callback so that the user has some visibility into long-running
@@ -274,7 +284,7 @@ func (s LinuxProvider) Mkfs(req MkfsReq) error {
274284
if req.Force {
275285
args = append([]string{"-F"}, args...)
276286
}
277-
out, err := exec.Command(cmdPath, args...).Output()
287+
out, err := s.runCommand(cmdPath, args...)
278288
if err != nil {
279289
return &RunCmdError{
280290
Wrapped: err,
@@ -301,7 +311,7 @@ func (s LinuxProvider) GetDeviceLabel(device string) (string, error) {
301311
}
302312

303313
args := []string{"-o", "label", "--noheadings", device}
304-
out, err := exec.Command(cmdPath, args...).Output()
314+
out, err := s.runCommand(cmdPath, args...)
305315
if err != nil {
306316
return "", &RunCmdError{
307317
Wrapped: err,
@@ -325,7 +335,7 @@ func (s LinuxProvider) Getfs(device string) (string, error) {
325335
}
326336

327337
args := []string{"-s", device}
328-
out, err := exec.Command(cmdPath, args...).Output()
338+
out, err := s.runCommand(cmdPath, args...)
329339
if err != nil {
330340
return FsTypeNone, &RunCmdError{
331341
Wrapped: err,

src/control/provider/system/system_linux_test.go

Lines changed: 104 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//
22
// (C) Copyright 2019-2024 Intel Corporation.
3-
// (C) Copyright 2025 Hewlett Packard Enterprise Development LP
3+
// (C) Copyright 2025-2026 Hewlett Packard Enterprise Development LP
44
//
55
// SPDX-License-Identifier: BSD-2-Clause-Patent
66
//
@@ -10,6 +10,7 @@ package system
1010
import (
1111
"errors"
1212
"os"
13+
"path/filepath"
1314
"regexp"
1415
"strings"
1516
"syscall"
@@ -202,39 +203,39 @@ func TestSystemLinux_GetfsType(t *testing.T) {
202203
}
203204
}
204205

205-
func TestSystemLinux_GetDeviceLabel(t *testing.T) {
206-
validDev := func(t *testing.T) string {
207-
t.Helper()
206+
func validDev(t *testing.T) string {
207+
t.Helper()
208208

209-
// Only want numbered partitions, not whole disks
210-
re := regexp.MustCompile(`^[a-zA-Z]+[0-9]+$`)
209+
// Only want numbered partitions, not whole disks
210+
re := regexp.MustCompile(`^[a-zA-Z]+[0-9]+$`)
211211

212-
sysRoot := "/sys/class/block/"
213-
entries, err := os.ReadDir(sysRoot)
214-
if err != nil {
215-
t.Fatalf("unable to read %q: %v", sysRoot, err)
216-
}
217-
218-
for _, entry := range entries {
219-
if !re.MatchString(entry.Name()) {
220-
continue
221-
}
212+
sysRoot := "/sys/class/block/"
213+
entries, err := os.ReadDir(sysRoot)
214+
if err != nil {
215+
t.Fatalf("unable to read %q: %v", sysRoot, err)
216+
}
222217

223-
devPath := "/dev/" + entry.Name()
224-
info, err := os.Stat(devPath)
225-
if err != nil {
226-
continue
227-
}
228-
if (info.Mode()&os.ModeDevice) != 0 && (info.Mode()&os.ModeCharDevice) == 0 {
229-
t.Logf("using block device %q for test", devPath)
230-
return devPath
231-
}
218+
for _, entry := range entries {
219+
if !re.MatchString(entry.Name()) {
220+
continue
232221
}
233222

234-
t.Fatal("no valid block device found for test")
235-
return ""
223+
devPath := "/dev/" + entry.Name()
224+
info, err := os.Stat(devPath)
225+
if err != nil {
226+
continue
227+
}
228+
if (info.Mode()&os.ModeDevice) != 0 && (info.Mode()&os.ModeCharDevice) == 0 {
229+
t.Logf("using block device %q for test", devPath)
230+
return devPath
231+
}
236232
}
237233

234+
t.Fatal("no valid block device found for test")
235+
return ""
236+
}
237+
238+
func TestSystemLinux_GetDeviceLabel(t *testing.T) {
238239
for name, tc := range map[string]struct {
239240
path string
240241
expErr error
@@ -312,3 +313,79 @@ func TestSystemLinux_fsStrFromMagic(t *testing.T) {
312313
})
313314
}
314315
}
316+
317+
func TestSystemLinux_Mkfs(t *testing.T) {
318+
for name, tc := range map[string]struct {
319+
req MkfsReq
320+
expErr error
321+
expCmdName string
322+
expCmdArgs []string
323+
}{
324+
"empty": {
325+
req: MkfsReq{},
326+
expErr: errors.New("no filesystem"),
327+
},
328+
"bad filesystem": {
329+
req: MkfsReq{
330+
Filesystem: "moo",
331+
},
332+
expErr: errors.New("unable to find mkfs.moo"),
333+
},
334+
"bad device": {
335+
req: MkfsReq{
336+
Filesystem: "ext4",
337+
Device: "/notreal",
338+
},
339+
expErr: syscall.ENOENT,
340+
},
341+
"success": {
342+
req: MkfsReq{
343+
Filesystem: "ext4",
344+
Device: validDev(t), // real device, but actual mkfs command is mocked
345+
},
346+
expCmdName: "mkfs.ext4",
347+
expCmdArgs: []string{validDev(t)},
348+
},
349+
"force": {
350+
req: MkfsReq{
351+
Filesystem: "ext4",
352+
Device: validDev(t),
353+
Force: true,
354+
},
355+
expCmdName: "mkfs.ext4",
356+
expCmdArgs: []string{"-F", validDev(t)},
357+
},
358+
"options": {
359+
req: MkfsReq{
360+
Filesystem: "ext4",
361+
Device: validDev(t),
362+
Options: []string{"-L", "my_device"},
363+
},
364+
expCmdName: "mkfs.ext4",
365+
expCmdArgs: []string{"-L", "my_device", validDev(t)},
366+
},
367+
} {
368+
t.Run(name, func(t *testing.T) {
369+
p := DefaultProvider()
370+
371+
var seenName string
372+
var seenArgs []string
373+
p.runCommand = func(name string, args ...string) ([]byte, error) {
374+
seenName = name
375+
seenArgs = args
376+
return []byte{}, nil
377+
}
378+
379+
err := p.Mkfs(tc.req)
380+
381+
test.CmpErr(t, tc.expErr, err)
382+
383+
if seenName != "" {
384+
// don't care where the binary was found, just that it was
385+
seenName = filepath.Base(seenName)
386+
}
387+
test.AssertEqual(t, tc.expCmdName, seenName, "mkfs command name")
388+
test.AssertEqual(t, tc.expCmdArgs, seenArgs, "mkfs args")
389+
})
390+
}
391+
}

src/control/server/storage/metadata/provider.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//
22
// (C) Copyright 2022-2024 Intel Corporation.
3-
// (C) Copyright 2025 Hewlett Packard Enterprise Development LP
3+
// (C) Copyright 2025-2026 Hewlett Packard Enterprise Development LP
44
//
55
// SPDX-License-Identifier: BSD-2-Clause-Patent
66
//
@@ -107,13 +107,16 @@ func (p *Provider) setupMountPoint(req storage.MetadataFormatRequest) error {
107107
return errors.Wrap(err, "checking existing device label")
108108
}
109109

110-
var opts []string
110+
opts := []string{
111+
// Quiet mode
112+
"-q",
113+
}
111114
if label != "" {
112115
p.log.Debugf("preserving existing device label %q for %q", label, req.Device)
113116
opts = append(opts, "-L", label)
114117
}
115118

116-
p.log.Debugf("formatting device %q", req.Device)
119+
p.log.Debugf("mkfs.%s %q with options: %s", defaultDevFS, req.Device, strings.Join(opts, " "))
117120
if err := p.sys.Mkfs(system.MkfsReq{
118121
Filesystem: defaultDevFS,
119122
Device: req.Device,

src/control/server/storage/metadata/provider_test.go

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//
22
// (C) Copyright 2022-2024 Intel Corporation.
3-
// (C) Copyright 2025 Hewlett Packard Enterprise Development LP
3+
// (C) Copyright 2025-2026 Hewlett Packard Enterprise Development LP
44
//
55
// SPDX-License-Identifier: BSD-2-Clause-Patent
66
//
@@ -110,7 +110,8 @@ func TestMetadata_Provider_Format(t *testing.T) {
110110
sysCfg: &system.MockSysConfig{
111111
GetfsTypeErr: []error{errors.New("mock GetfsType")},
112112
},
113-
expMkfs: true,
113+
expMkfsOpts: []string{"-q"},
114+
expMkfs: true,
114115
},
115116
"GetfsType retries with parent if dir doesn't exist": {
116117
req: pathReq,
@@ -145,16 +146,18 @@ func TestMetadata_Provider_Format(t *testing.T) {
145146
sysCfg: &system.MockSysConfig{
146147
MkfsErr: errors.New("mock mkfs"),
147148
},
148-
expErr: errors.New("mock mkfs"),
149-
expMkfs: true,
149+
expErr: errors.New("mock mkfs"),
150+
expMkfsOpts: []string{"-q"},
151+
expMkfs: true,
150152
},
151153
"Mount fails": {
152154
req: deviceReq,
153155
mountCfg: &storage.MockMountProviderConfig{
154156
MountErr: errors.New("mock Mount"),
155157
},
156-
expErr: errors.New("mock Mount"),
157-
expMkfs: true,
158+
expErr: errors.New("mock Mount"),
159+
expMkfsOpts: []string{"-q"},
160+
expMkfs: true,
158161
},
159162
"remove old data dir fails": {
160163
req: deviceReq,
@@ -172,8 +175,9 @@ func TestMetadata_Provider_Format(t *testing.T) {
172175
}
173176
}
174177
},
175-
expErr: errors.New("removing old control metadata subdirectory"),
176-
expMkfs: true,
178+
expErr: errors.New("removing old control metadata subdirectory"),
179+
expMkfsOpts: []string{"-q"},
180+
expMkfs: true,
177181
},
178182
"create data dir fails": {
179183
req: deviceReq,
@@ -191,36 +195,40 @@ func TestMetadata_Provider_Format(t *testing.T) {
191195
}
192196
}
193197
},
194-
expErr: errors.New("creating control metadata subdirectory"),
195-
expMkfs: true,
198+
expErr: errors.New("creating control metadata subdirectory"),
199+
expMkfsOpts: []string{"-q"},
200+
expMkfs: true,
196201
},
197202
"chown data dir fails": {
198203
req: deviceReq,
199204
sysCfg: &system.MockSysConfig{
200205
ChownErr: errors.New("mock chown"),
201206
},
202-
expErr: errors.New("mock chown"),
203-
expMkfs: true,
207+
expErr: errors.New("mock chown"),
208+
expMkfsOpts: []string{"-q"},
209+
expMkfs: true,
204210
},
205211
"Unmount fails": {
206212
req: deviceReq,
207213
mountCfg: &storage.MockMountProviderConfig{
208214
IsMountedRes: true,
209215
UnmountErr: errors.New("mock Unmount"),
210216
},
211-
expErr: errors.New("mock Unmount"),
212-
expMkfs: true,
217+
expErr: errors.New("mock Unmount"),
218+
expMkfsOpts: []string{"-q"},
219+
expMkfs: true,
213220
},
214221
"device success": {
215-
req: deviceReq,
216-
expMkfs: true,
222+
req: deviceReq,
223+
expMkfsOpts: []string{"-q"},
224+
expMkfs: true,
217225
},
218226
"preserve existing label": {
219227
req: deviceReq,
220228
sysCfg: &system.MockSysConfig{
221229
GetDeviceLabelRes: "old_label",
222230
},
223-
expMkfsOpts: []string{"-L", "old_label"},
231+
expMkfsOpts: []string{"-q", "-L", "old_label"},
224232
expMkfs: true,
225233
},
226234
"path only doesn't attempt device format": {

0 commit comments

Comments
 (0)