Skip to content

Commit e743134

Browse files
committed
defined a common interface for nativeimgutil & qemuimgutil
Signed-off-by: Praful Khanduri <[email protected]>
1 parent dbf3cd9 commit e743134

File tree

14 files changed

+347
-79
lines changed

14 files changed

+347
-79
lines changed

cmd/limactl/disk.go

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ import (
1818
"github.com/sirupsen/logrus"
1919
"github.com/spf13/cobra"
2020

21-
"github.com/lima-vm/lima/pkg/nativeimgutil"
22-
"github.com/lima-vm/lima/pkg/qemu/imgutil"
21+
"github.com/lima-vm/lima/pkg/imgutil/proxyimgutil"
2322
"github.com/lima-vm/lima/pkg/store"
2423
"github.com/lima-vm/lima/pkg/store/filenames"
2524
)
@@ -113,11 +112,8 @@ func diskCreateAction(cmd *cobra.Command, args []string) error {
113112

114113
// qemu may not be available, use it only if needed.
115114
dataDisk := filepath.Join(diskDir, filenames.DataDisk)
116-
if format == "raw" {
117-
err = nativeimgutil.CreateRawDisk(dataDisk, int(diskSize))
118-
} else {
119-
err = imgutil.CreateDisk(dataDisk, format, int(diskSize))
120-
}
115+
diskUtil := proxyimgutil.NewProxyImageUtil()
116+
err = diskUtil.CreateDisk(dataDisk, int(diskSize))
121117
if err != nil {
122118
rerr := os.RemoveAll(diskDir)
123119
if rerr != nil {
@@ -410,11 +406,8 @@ func diskResizeAction(cmd *cobra.Command, args []string) error {
410406

411407
// qemu may not be available, use it only if needed.
412408
dataDisk := filepath.Join(disk.Dir, filenames.DataDisk)
413-
if disk.Format == "raw" {
414-
err = nativeimgutil.ResizeRawDisk(dataDisk, int(diskSize))
415-
} else {
416-
err = imgutil.ResizeDisk(dataDisk, disk.Format, int(diskSize))
417-
}
409+
diskUtil := proxyimgutil.NewProxyImageUtil()
410+
err = diskUtil.ResizeDisk(dataDisk, int(diskSize))
418411
if err != nil {
419412
return fmt.Errorf("failed to resize disk %q: %w", diskName, err)
420413
}

pkg/imgutil/interface.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// SPDX-FileCopyrightText: Copyright The Lima Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package imgutil
5+
6+
import (
7+
"encoding/json"
8+
"os"
9+
)
10+
11+
// ImageDiskManager defines the common operations for disk image utilities.
12+
type ImageDiskManager interface {
13+
// CreateDisk creates a new disk image with the specified size.
14+
CreateDisk(disk string, size int) error
15+
16+
// ResizeDisk resizes an existing disk image to the specified size.
17+
ResizeDisk(disk string, size int) error
18+
19+
// ConvertToRaw converts a disk image to raw format.
20+
ConvertToRaw(source, dest string, size *int64, allowSourceWithBackingFile bool) error
21+
22+
// MakeSparse makes a file sparse, starting from the specified offset.
23+
MakeSparse(f *os.File, offset int64) error
24+
}
25+
26+
type InfoFormatSpecific struct {
27+
Type string `json:"type,omitempty"` // since QEMU 1.7
28+
Data json.RawMessage `json:"data,omitempty"` // since QEMU 1.7
29+
}
30+
31+
func (sp *InfoFormatSpecific) Qcow2() *InfoFormatSpecificDataQcow2 {
32+
if sp.Type != "qcow2" {
33+
return nil
34+
}
35+
var x InfoFormatSpecificDataQcow2
36+
if err := json.Unmarshal(sp.Data, &x); err != nil {
37+
panic(err)
38+
}
39+
return &x
40+
}
41+
42+
func (sp *InfoFormatSpecific) Vmdk() *InfoFormatSpecificDataVmdk {
43+
if sp.Type != "vmdk" {
44+
return nil
45+
}
46+
var x InfoFormatSpecificDataVmdk
47+
if err := json.Unmarshal(sp.Data, &x); err != nil {
48+
panic(err)
49+
}
50+
return &x
51+
}
52+
53+
type InfoFormatSpecificDataQcow2 struct {
54+
Compat string `json:"compat,omitempty"` // since QEMU 1.7
55+
LazyRefcounts bool `json:"lazy-refcounts,omitempty"` // since QEMU 1.7
56+
Corrupt bool `json:"corrupt,omitempty"` // since QEMU 2.2
57+
RefcountBits int `json:"refcount-bits,omitempty"` // since QEMU 2.3
58+
CompressionType string `json:"compression-type,omitempty"` // since QEMU 5.1
59+
ExtendedL2 bool `json:"extended-l2,omitempty"` // since QEMU 5.2
60+
}
61+
62+
type InfoFormatSpecificDataVmdk struct {
63+
CreateType string `json:"create-type,omitempty"` // since QEMU 1.7
64+
CID int `json:"cid,omitempty"` // since QEMU 1.7
65+
ParentCID int `json:"parent-cid,omitempty"` // since QEMU 1.7
66+
Extents []InfoFormatSpecificDataVmdkExtent `json:"extents,omitempty"` // since QEMU 1.7
67+
}
68+
69+
type InfoFormatSpecificDataVmdkExtent struct {
70+
Filename string `json:"filename,omitempty"` // since QEMU 1.7
71+
Format string `json:"format,omitempty"` // since QEMU 1.7
72+
VSize int64 `json:"virtual-size,omitempty"` // since QEMU 1.7
73+
ClusterSize int `json:"cluster-size,omitempty"` // since QEMU 1.7
74+
}

pkg/nativeimgutil/fuzz_test.go renamed to pkg/imgutil/nativeimgutil/fuzz_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@ func FuzzConvertToRaw(f *testing.F) {
1717
destPath := filepath.Join(t.TempDir(), "dest.img")
1818
err := os.WriteFile(srcPath, imgData, 0o600)
1919
assert.NilError(t, err)
20-
_ = ConvertToRaw(srcPath, destPath, &size, withBacking)
20+
_ = convertToRaw(srcPath, destPath, &size, withBacking)
2121
})
2222
}

pkg/nativeimgutil/nativeimgutil.go renamed to pkg/imgutil/nativeimgutil/nativeimgutil.go

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,22 @@ import (
2828
// aligned to 512 bytes.
2929
const sectorSize = 512
3030

31-
// RoundUp rounds size up to sectorSize.
32-
func RoundUp(size int) int {
31+
// NativeImageUtil is the native implementation of the imgutil Interface.
32+
type NativeImageUtil struct{}
33+
34+
// NewNativeImageUtil returns a new NativeImageUtil instance.
35+
func NewNativeImageUtil() *NativeImageUtil {
36+
return &NativeImageUtil{}
37+
}
38+
39+
// roundUp rounds size up to sectorSize.
40+
func roundUp(size int) int {
3341
sectors := (size + sectorSize - 1) / sectorSize
3442
return sectors * sectorSize
3543
}
3644

37-
// CreateRawDisk creates an empty raw data disk.
38-
func CreateRawDisk(disk string, size int) error {
45+
// createRawDisk creates an empty raw data disk.
46+
func createRawDisk(disk string, size int) error {
3947
if _, err := os.Stat(disk); err == nil || !errors.Is(err, fs.ErrNotExist) {
4048
return err
4149
}
@@ -44,20 +52,20 @@ func CreateRawDisk(disk string, size int) error {
4452
return err
4553
}
4654
defer f.Close()
47-
roundedSize := RoundUp(size)
55+
roundedSize := roundUp(size)
4856
return f.Truncate(int64(roundedSize))
4957
}
5058

51-
// ResizeRawDisk resizes a raw data disk.
52-
func ResizeRawDisk(disk string, size int) error {
53-
roundedSize := RoundUp(size)
59+
// resizeRawDisk resizes a raw data disk.
60+
func resizeRawDisk(disk string, size int) error {
61+
roundedSize := roundUp(size)
5462
return os.Truncate(disk, int64(roundedSize))
5563
}
5664

57-
// ConvertToRaw converts a source disk into a raw disk.
65+
// convertToRaw converts a source disk into a raw disk.
5866
// source and dest may be same.
59-
// ConvertToRaw is a NOP if source == dest, and no resizing is needed.
60-
func ConvertToRaw(source, dest string, size *int64, allowSourceWithBackingFile bool) error {
67+
// convertToRaw is a NOP if source == dest, and no resizing is needed.
68+
func convertToRaw(source, dest string, size *int64, allowSourceWithBackingFile bool) error {
6169
srcF, err := os.Open(source)
6270
if err != nil {
6371
return err
@@ -106,7 +114,7 @@ func ConvertToRaw(source, dest string, size *int64, allowSourceWithBackingFile b
106114
// Truncating before copy eliminates the seeks during copy and provide a
107115
// hint to the file system that may minimize allocations and fragmentation
108116
// of the file.
109-
if err := MakeSparse(destTmpF, srcImg.Size()); err != nil {
117+
if err := makeSparse(destTmpF, srcImg.Size()); err != nil {
110118
return err
111119
}
112120

@@ -125,7 +133,7 @@ func ConvertToRaw(source, dest string, size *int64, allowSourceWithBackingFile b
125133
// Resize
126134
if size != nil {
127135
logrus.Infof("Expanding to %s", units.BytesSize(float64(*size)))
128-
if err = MakeSparse(destTmpF, *size); err != nil {
136+
if err = makeSparse(destTmpF, *size); err != nil {
129137
return err
130138
}
131139
}
@@ -153,7 +161,7 @@ func convertRawToRaw(source, dest string, size *int64) error {
153161
if err != nil {
154162
return err
155163
}
156-
if err = MakeSparse(destF, *size); err != nil {
164+
if err = makeSparse(destF, *size); err != nil {
157165
_ = destF.Close()
158166
return err
159167
}
@@ -162,7 +170,7 @@ func convertRawToRaw(source, dest string, size *int64) error {
162170
return nil
163171
}
164172

165-
func MakeSparse(f *os.File, n int64) error {
173+
func makeSparse(f *os.File, n int64) error {
166174
if _, err := f.Seek(n, io.SeekStart); err != nil {
167175
return err
168176
}

pkg/nativeimgutil/nativeimgutil_test.go renamed to pkg/imgutil/nativeimgutil/nativeimgutil_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func TestRoundUp(t *testing.T) {
2525
{123456789, 123457024},
2626
}
2727
for _, tc := range tests {
28-
if RoundUp(tc.Size) != tc.Rounded {
28+
if roundUp(tc.Size) != tc.Rounded {
2929
t.Errorf("expected %d, got %d", tc.Rounded, tc.Size)
3030
}
3131
}
@@ -63,15 +63,15 @@ func TestConvertToRaw(t *testing.T) {
6363
t.Run("qcow without backing file", func(t *testing.T) {
6464
resultImage := filepath.Join(tmpDir, strings.ReplaceAll(strings.ReplaceAll(t.Name(), string(os.PathSeparator), "_"), "/", "_"))
6565

66-
err = ConvertToRaw(qcowImage.Name(), resultImage, nil, false)
66+
err = convertToRaw(qcowImage.Name(), resultImage, nil, false)
6767
assert.NilError(t, err)
6868
assertFileEquals(t, rawImage.Name(), resultImage)
6969
})
7070

7171
t.Run("qcow with backing file", func(t *testing.T) {
7272
resultImage := filepath.Join(tmpDir, strings.ReplaceAll(strings.ReplaceAll(t.Name(), string(os.PathSeparator), "_"), "/", "_"))
7373

74-
err = ConvertToRaw(qcowImage.Name(), resultImage, nil, true)
74+
err = convertToRaw(qcowImage.Name(), resultImage, nil, true)
7575
assert.NilError(t, err)
7676
assertFileEquals(t, rawImage.Name(), resultImage)
7777
})
@@ -80,15 +80,15 @@ func TestConvertToRaw(t *testing.T) {
8080
resultImage := filepath.Join(tmpDir, strings.ReplaceAll(strings.ReplaceAll(t.Name(), string(os.PathSeparator), "_"), "/", "_"))
8181

8282
size := int64(2_097_152) // 2mb
83-
err = ConvertToRaw(qcowImage.Name(), resultImage, &size, false)
83+
err = convertToRaw(qcowImage.Name(), resultImage, &size, false)
8484
assert.NilError(t, err)
8585
assertFileEquals(t, rawImageExtended.Name(), resultImage)
8686
})
8787

8888
t.Run("raw", func(t *testing.T) {
8989
resultImage := filepath.Join(tmpDir, strings.ReplaceAll(strings.ReplaceAll(t.Name(), string(os.PathSeparator), "_"), "/", "_"))
9090

91-
err = ConvertToRaw(rawImage.Name(), resultImage, nil, false)
91+
err = convertToRaw(rawImage.Name(), resultImage, nil, false)
9292
assert.NilError(t, err)
9393
assertFileEquals(t, rawImage.Name(), resultImage)
9494
})

pkg/imgutil/nativeimgutil/util.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// SPDX-FileCopyrightText: Copyright The Lima Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package nativeimgutil
5+
6+
import (
7+
"os"
8+
)
9+
10+
// CreateDisk creates a new raw disk image with the specified size.
11+
func (n *NativeImageUtil) CreateDisk(disk string, size int) error {
12+
return createRawDisk(disk, size)
13+
}
14+
15+
// ResizeDisk resizes an existing raw disk image to the specified size.
16+
func (n *NativeImageUtil) ResizeDisk(disk string, size int) error {
17+
return resizeRawDisk(disk, size)
18+
}
19+
20+
// ConvertToRaw converts a disk image to raw format.
21+
func (n *NativeImageUtil) ConvertToRaw(source, dest string, size *int64, allowSourceWithBackingFile bool) error {
22+
return convertToRaw(source, dest, size, allowSourceWithBackingFile)
23+
}
24+
25+
func (n *NativeImageUtil) MakeSparse(f *os.File, offset int64) error {
26+
return makeSparse(f, offset)
27+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// SPDX-FileCopyrightText: Copyright The Lima Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package proxyimgutil
5+
6+
import (
7+
"errors"
8+
"os"
9+
"os/exec"
10+
11+
"github.com/lima-vm/lima/pkg/imgutil"
12+
"github.com/lima-vm/lima/pkg/imgutil/nativeimgutil"
13+
"github.com/lima-vm/lima/pkg/imgutil/qemuimgutil"
14+
)
15+
16+
type ProxyImageDiskManager struct {
17+
qemu imgutil.ImageDiskManager
18+
native imgutil.ImageDiskManager
19+
}
20+
21+
func NewProxyImageUtil() imgutil.ImageDiskManager {
22+
return &ProxyImageDiskManager{
23+
qemu: qemuimgutil.NewQemuImageUtil(),
24+
native: nativeimgutil.NewNativeImageUtil(),
25+
}
26+
}
27+
28+
func (p *ProxyImageDiskManager) CreateDisk(disk string, size int) error {
29+
if err := p.qemu.CreateDisk(disk, size); err != nil {
30+
if errors.Is(err, exec.ErrNotFound) {
31+
return p.native.CreateDisk(disk, size)
32+
}
33+
return err
34+
}
35+
return nil
36+
}
37+
38+
func (p *ProxyImageDiskManager) ResizeDisk(disk string, size int) error {
39+
if err := p.qemu.ResizeDisk(disk, size); err != nil {
40+
if errors.Is(err, exec.ErrNotFound) {
41+
return p.native.ResizeDisk(disk, size)
42+
}
43+
return err
44+
}
45+
return nil
46+
}
47+
48+
func (p *ProxyImageDiskManager) ConvertToRaw(source, dest string, size *int64, allowSourceWithBackingFile bool) error {
49+
if err := p.qemu.ConvertToRaw(source, dest, size, allowSourceWithBackingFile); err != nil {
50+
if errors.Is(err, exec.ErrNotFound) {
51+
return p.native.ConvertToRaw(source, dest, size, allowSourceWithBackingFile)
52+
}
53+
return err
54+
}
55+
return nil
56+
}
57+
58+
func (p *ProxyImageDiskManager) MakeSparse(f *os.File, offset int64) error {
59+
if err := p.qemu.MakeSparse(f, offset); err != nil {
60+
if errors.Is(err, exec.ErrNotFound) {
61+
return p.native.MakeSparse(f, offset)
62+
}
63+
return err
64+
}
65+
return nil
66+
}

0 commit comments

Comments
 (0)