Skip to content

Commit 0e8dc36

Browse files
committed
chore: tests
Signed-off-by: obarisk <obarisk@gmail.com>
1 parent 23c61d6 commit 0e8dc36

1 file changed

Lines changed: 126 additions & 0 deletions

File tree

storage/drivers/btrfs/btrfs_test.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@
33
package btrfs
44

55
import (
6+
"bytes"
7+
"fmt"
68
"os"
9+
"os/exec"
710
"path"
11+
"strings"
812
"testing"
913

1014
graphdriver "go.podman.io/storage/drivers"
@@ -77,3 +81,125 @@ func TestBtrfsListLayers(t *testing.T) {
7781
func TestBtrfsTeardown(t *testing.T) {
7882
graphtest.PutDriver(t)
7983
}
84+
85+
// dependencies: btrfs-progs, libbtrfs-dev
86+
// permission: root, loop device
87+
func TestSubVolDelete(t *testing.T) {
88+
if os.Getuid() != 0 {
89+
t.Skip("test requires root")
90+
}
91+
92+
// Helpers
93+
94+
runCmd := func(name string, arg ...string) error {
95+
cmd := exec.Command(name, arg...)
96+
var stderr bytes.Buffer
97+
cmd.Stderr = &stderr
98+
if err := cmd.Run(); err != nil {
99+
return fmt.Errorf("command '%s %s' failed: %v, stderr: %s", name, strings.Join(arg, " "), err, stderr.String())
100+
}
101+
return nil
102+
}
103+
104+
qgroupShow := func(mountPath string) string {
105+
cmd := exec.Command("btrfs", "qgroup", "show", mountPath)
106+
var out bytes.Buffer
107+
cmd.Stdout = &out
108+
cmd.Run()
109+
return out.String()
110+
}
111+
112+
// parepare btrfs using loop device
113+
baseDir, err := os.MkdirTemp("/mnt", "btrfs-test-")
114+
if err != nil {
115+
t.Fatalf("Failed to create base temp dir: %v", err)
116+
}
117+
defer os.RemoveAll(baseDir)
118+
119+
mnt := path.Join(baseDir, "mountpoint")
120+
if err := os.Mkdir(mnt, 755); err != nil {
121+
t.Fatalf("Failed to create mountpoint dir: %v", err)
122+
}
123+
124+
blockFile := path.Join(baseDir, "btrfs.img")
125+
if err := runCmd("dd", "if=/dev/zero", "of="+blockFile, "bs=1M", "count=120", "status=none"); err != nil {
126+
t.Skipf("Failed to create block file: %v", err)
127+
}
128+
129+
if err := runCmd("mkfs.btrfs", "-f", blockFile); err != nil {
130+
t.Skipf("Failed to format to btrfs: %v", err)
131+
}
132+
133+
findLoopCmd := exec.Command("losetup", "-f")
134+
var loopDevBytes bytes.Buffer
135+
findLoopCmd.Stdout = &loopDevBytes
136+
if err := findLoopCmd.Run(); err != nil {
137+
t.Skipf("Failed to find a loop device with 'losetup -f': %v", err)
138+
}
139+
loopDev := strings.TrimSpace(loopDevBytes.String())
140+
if loopDev == "" {
141+
t.Skip("No available loop devices found")
142+
}
143+
144+
if err := runCmd("losetup", loopDev, blockFile); err != nil {
145+
t.Skipf("Failed to setup loop device with 'losetup %s %s': %v", loopDev, blockFile, err)
146+
}
147+
defer runCmd("losetup", "-d", loopDev)
148+
149+
if err := runCmd("mount", loopDev, mnt); err != nil {
150+
t.Skipf("Failed to mount loop device %s: %v", loopDev, err)
151+
}
152+
defer runCmd("umount", mnt)
153+
154+
d := &Driver{home: mnt}
155+
if err := d.enableQuota(); err != nil {
156+
t.Skipf("Failed to enable qgroup using API: %v", err)
157+
}
158+
159+
t.Run("subVolDelete", func(t *testing.T) {
160+
subvolName := "subvol1"
161+
subvolPath := path.Join(mnt, subvolName)
162+
if err := subvolCreate(mnt, subvolName); err != nil {
163+
t.Fatalf("Failed to create subvolume using API: %v", err)
164+
}
165+
166+
if err := d.subvolRescanQuota(); err != nil {
167+
t.Fatalf("Failed to rescan quota using API: %v", err)
168+
}
169+
170+
qtreeid, err := subvolLookupQgroup(subvolPath)
171+
if err != nil {
172+
t.Fatalf("Failed to lookup qgroup for subvolume using API: %v", err)
173+
}
174+
qgroupID := fmt.Sprintf("0/%d", qtreeid)
175+
t.Logf("subvolume %s has qgroup ID %s", subvolPath, qgroupID)
176+
177+
if !strings.Contains(qgroupShow(mnt), qgroupID) {
178+
t.Fatalf("qgroup %s was not created for subvolume %s", qgroupID, subvolPath)
179+
}
180+
181+
if err := subvolDelete(mnt, subvolName, true); err != nil {
182+
t.Fatalf("Failed to delete subvolume using API: %v", err)
183+
}
184+
185+
if err := d.subvolRescanQuota(); err != nil {
186+
t.Fatalf("Failed to rescan quota after delete using API: %v", err)
187+
}
188+
189+
qgroupInfo := qgroupShow(mnt)
190+
t.Logf("Current qgroup info:\n%s", qgroupInfo)
191+
192+
if strings.Contains(qgroupInfo, qgroupID) && !strings.Contains(qgroupInfo, "under deletion") {
193+
t.Fatalf("qgroup %s was not marked as 'under deletion' for subvolume %s", qgroupID, subvolPath)
194+
}
195+
196+
runCmd("btrfs", "qgroup", "clear-stale", mnt)
197+
198+
qgroupInfo = qgroupShow(mnt)
199+
t.Logf("Current qgroup info after clearing stale:\n%s", qgroupInfo)
200+
201+
if strings.Contains(qgroupInfo, qgroupID) {
202+
t.Fatalf("qgroup %s was not deleted for subvolume %s", qgroupID, subvolPath)
203+
}
204+
})
205+
}

0 commit comments

Comments
 (0)