containerized-data-importer/pkg/importer/data-processor_test.go
Eng Zer Jun aaacbae797
refactor: move from io/ioutil to io and os packages (#2484)
The io/ioutil package has been deprecated as of Go 1.16 [1]. This commit
replaces the existing io/ioutil functions with their new definitions in
io and os packages.

[1]: https://golang.org/doc/go1.16#ioutil
Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
2022-12-05 19:19:13 +00:00

640 lines
23 KiB
Go

package importer
import (
"fmt"
"net/url"
"os"
. "github.com/onsi/ginkgo"
"github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/gomega"
"k8s.io/apimachinery/pkg/api/resource"
"github.com/pkg/errors"
"kubevirt.io/containerized-data-importer/pkg/image"
)
type fakeInfoOpRetVal struct {
imgInfo *image.ImgInfo
e error
}
const TestImagesDir = "../../tests/images"
const (
SmallActualSize = 1024 * 1024
SmallVirtualSize = 1024 * 1024
)
var (
fakeSmallImageInfo = image.ImgInfo{Format: "", BackingFile: "", VirtualSize: SmallVirtualSize, ActualSize: SmallActualSize}
fakeZeroImageInfo = image.ImgInfo{Format: "", BackingFile: "", VirtualSize: 0, ActualSize: 0}
fakeInfoRet = fakeInfoOpRetVal{imgInfo: &fakeSmallImageInfo, e: nil}
)
type fakeQEMUOperations struct {
e2 error
e3 error
ret4 fakeInfoOpRetVal
e5 error
e6 error
resizeQuantity *resource.Quantity
}
type MockDataProvider struct {
infoResponse ProcessingPhase
transferResponse ProcessingPhase
url *url.URL
transferPath string
transferFile string
calledPhases []ProcessingPhase
needsScratch bool
}
// Info is called to get initial information about the data
func (m *MockDataProvider) Info() (ProcessingPhase, error) {
m.calledPhases = append(m.calledPhases, ProcessingPhaseInfo)
if m.infoResponse == ProcessingPhaseError {
return ProcessingPhaseError, errors.New("Info errored")
}
return m.infoResponse, nil
}
// Transfer is called to transfer the data from the source to the passed in path.
func (m *MockDataProvider) Transfer(path string) (ProcessingPhase, error) {
m.calledPhases = append(m.calledPhases, m.infoResponse)
m.transferPath = path
if m.transferResponse == ProcessingPhaseError {
if m.needsScratch {
return ProcessingPhaseError, ErrInvalidPath
}
return ProcessingPhaseError, errors.New("Transfer errored")
}
return m.transferResponse, nil
}
// TransferFile is called to transfer the data from the source to the passed in file.
func (m *MockDataProvider) TransferFile(fileName string) (ProcessingPhase, error) {
m.calledPhases = append(m.calledPhases, ProcessingPhaseTransferDataFile)
m.transferFile = fileName
if m.transferResponse == ProcessingPhaseError {
return ProcessingPhaseError, errors.New("TransferFile errored")
}
return m.transferResponse, nil
}
// Geturl returns the url that the data processor can use when converting the data.
func (m *MockDataProvider) GetURL() *url.URL {
return m.url
}
// Close closes any readers or other open resources.
func (m *MockDataProvider) Close() error {
return nil
}
type MockAsyncDataProvider struct {
MockDataProvider
ResumePhase ProcessingPhase
}
// Info is called to get initial information about the data.
func (madp *MockAsyncDataProvider) Info() (ProcessingPhase, error) {
return madp.MockDataProvider.Info()
}
// Transfer is called to transfer the data from the source to the passed in path.
func (madp *MockAsyncDataProvider) Transfer(path string) (ProcessingPhase, error) {
return madp.MockDataProvider.Transfer(path)
}
// TransferFile is called to transfer the data from the source to the passed in file.
func (madp *MockAsyncDataProvider) TransferFile(fileName string) (ProcessingPhase, error) {
return madp.MockDataProvider.TransferFile(fileName)
}
// Close closes any readers or other open resources.
func (madp *MockAsyncDataProvider) Close() error {
return madp.MockDataProvider.Close()
}
// GetURL returns the url that the data processor can use when converting the data.
func (madp *MockAsyncDataProvider) GetURL() *url.URL {
return madp.MockDataProvider.GetURL()
}
// GetResumePhase returns the next phase to process when resuming
func (madp *MockAsyncDataProvider) GetResumePhase() ProcessingPhase {
return madp.ResumePhase
}
const ProcessingPhaseFoo ProcessingPhase = "Foo"
type MockCustomizedDataProvider struct {
MockDataProvider
fooResponse ProcessingPhase
}
func (mcdp *MockCustomizedDataProvider) Foo() (ProcessingPhase, error) {
mcdp.calledPhases = append(mcdp.calledPhases, ProcessingPhaseFoo)
return mcdp.fooResponse, nil
}
var _ = Describe("Data Processor", func() {
It("should call the right phases based on the responses from the provider, Transfer should pass the scratch data dir as a path", func() {
mdp := &MockDataProvider{
infoResponse: ProcessingPhaseTransferScratch,
transferResponse: ProcessingPhaseComplete,
}
dp := NewDataProcessor(mdp, "dest", "dataDir", "scratchDataDir", "1G", 0.055, false)
err := dp.ProcessData()
Expect(err).ToNot(HaveOccurred())
Expect(2).To(Equal(len(mdp.calledPhases)))
Expect(ProcessingPhaseInfo).To(Equal(mdp.calledPhases[0]))
Expect(ProcessingPhaseTransferScratch).To(Equal(mdp.calledPhases[1]))
Expect("scratchDataDir").To(Equal(mdp.transferPath))
})
It("should call the right phases based on the responses from the provider, TransferTarget should pass the data dir as a path", func() {
mdp := &MockDataProvider{
infoResponse: ProcessingPhaseTransferDataDir,
transferResponse: ProcessingPhaseComplete,
}
dp := NewDataProcessor(mdp, "dest", "dataDir", "scratchDataDir", "1G", 0.055, false)
err := dp.ProcessData()
Expect(err).ToNot(HaveOccurred())
Expect(2).To(Equal(len(mdp.calledPhases)))
Expect(ProcessingPhaseInfo).To(Equal(mdp.calledPhases[0]))
Expect(ProcessingPhaseTransferDataDir).To(Equal(mdp.calledPhases[1]))
Expect("dataDir").To(Equal(mdp.transferPath))
})
It("should error on Transfer phase", func() {
mdp := &MockDataProvider{
infoResponse: ProcessingPhaseTransferScratch,
transferResponse: ProcessingPhaseError,
}
dp := NewDataProcessor(mdp, "dest", "dataDir", "scratchDataDir", "1G", 0.055, false)
err := dp.ProcessData()
Expect(err).To(HaveOccurred())
Expect(2).To(Equal(len(mdp.calledPhases)))
Expect(ProcessingPhaseInfo).To(Equal(mdp.calledPhases[0]))
Expect(ProcessingPhaseTransferScratch).To(Equal(mdp.calledPhases[1]))
})
It("should error on Transfer phase if scratch space is required", func() {
mdp := &MockDataProvider{
infoResponse: ProcessingPhaseTransferScratch,
transferResponse: ProcessingPhaseError,
needsScratch: true,
}
dp := NewDataProcessor(mdp, "dest", "dataDir", "scratchDataDir", "1G", 0.055, false)
err := dp.ProcessData()
Expect(err).To(HaveOccurred())
Expect(ErrRequiresScratchSpace).To(Equal(err))
Expect(2).To(Equal(len(mdp.calledPhases)))
Expect(ProcessingPhaseInfo).To(Equal(mdp.calledPhases[0]))
Expect(ProcessingPhaseTransferScratch).To(Equal(mdp.calledPhases[1]))
})
It("should call the right phases based on the responses from the provider, TransferDataFile should pass the data file", func() {
mdp := &MockDataProvider{
infoResponse: ProcessingPhaseTransferDataFile,
transferResponse: ProcessingPhaseComplete,
}
dp := NewDataProcessor(mdp, "dest", "dataDir", "scratchDataDir", "1G", 0.055, false)
qemuOperations := NewFakeQEMUOperations(nil, nil, fakeInfoOpRetVal{&fakeZeroImageInfo, errors.New("Scratch space required, and none found ")}, nil, nil, nil)
replaceQEMUOperations(qemuOperations, func() {
err := dp.ProcessData()
Expect(err).ToNot(HaveOccurred())
Expect(2).To(Equal(len(mdp.calledPhases)))
Expect(ProcessingPhaseInfo).To(Equal(mdp.calledPhases[0]))
Expect(ProcessingPhaseTransferDataFile).To(Equal(mdp.calledPhases[1]))
})
})
It("should fail when TransferDataFile fails", func() {
mdp := &MockDataProvider{
infoResponse: ProcessingPhaseTransferDataFile,
transferResponse: ProcessingPhaseError,
}
dp := NewDataProcessor(mdp, "dest", "dataDir", "scratchDataDir", "1G", 0.055, false)
qemuOperations := NewQEMUAllErrors()
replaceQEMUOperations(qemuOperations, func() {
err := dp.ProcessData()
Expect(err).To(HaveOccurred())
Expect(2).To(Equal(len(mdp.calledPhases)))
Expect(ProcessingPhaseInfo).To(Equal(mdp.calledPhases[0]))
Expect(ProcessingPhaseTransferDataFile).To(Equal(mdp.calledPhases[1]))
})
})
It("should error on Unknown phase", func() {
mdp := &MockDataProvider{
infoResponse: ProcessingPhase("invalidphase"),
}
dp := NewDataProcessor(mdp, "dest", "dataDir", "scratchDataDir", "1G", 0.055, false)
err := dp.ProcessData()
Expect(err).To(HaveOccurred())
Expect(1).To(Equal(len(mdp.calledPhases)))
Expect(ProcessingPhaseInfo).To(Equal(mdp.calledPhases[0]))
})
It("should call Convert after Process phase", func() {
tmpDir, err := os.MkdirTemp("", "scratch")
Expect(err).ToNot(HaveOccurred())
defer os.RemoveAll(tmpDir)
url, err := url.Parse("http://fakeurl-notreal.fake")
Expect(err).ToNot(HaveOccurred())
mdp := &MockDataProvider{
infoResponse: ProcessingPhaseTransferScratch,
transferResponse: ProcessingPhaseConvert,
url: url,
}
dp := NewDataProcessor(mdp, "", "dataDir", tmpDir, "1G", 0.055, false)
dp.availableSpace = int64(1536000)
usableSpace := dp.getUsableSpace()
qemuOperations := NewFakeQEMUOperations(nil, nil, fakeInfoRet, nil, nil, resource.NewScaledQuantity(usableSpace, 1024*1024))
replaceQEMUOperations(qemuOperations, func() {
err = dp.ProcessData()
Expect(err).ToNot(HaveOccurred())
Expect(2).To(Equal(len(mdp.calledPhases)))
Expect(ProcessingPhaseInfo).To(Equal(mdp.calledPhases[0]))
Expect(ProcessingPhaseTransferScratch).To(Equal(mdp.calledPhases[1]))
Expect(tmpDir).To(Equal(mdp.transferPath))
})
})
It("should allow phase regsitry", func() {
mcdp := &MockCustomizedDataProvider{
MockDataProvider: MockDataProvider{
infoResponse: ProcessingPhaseTransferDataDir,
transferResponse: ProcessingPhaseFoo,
},
fooResponse: ProcessingPhaseComplete,
}
dp := NewDataProcessor(mcdp, "dest", "dataDir", "scratchDataDir", "1G", 0.055, false)
dp.RegisterPhaseExecutor(ProcessingPhaseFoo, func() (ProcessingPhase, error) {
return mcdp.Foo()
})
err := dp.ProcessData()
fmt.Println(mcdp.calledPhases)
Expect(err).ToNot(HaveOccurred())
Expect(3).To(Equal(len(mcdp.calledPhases)))
Expect(ProcessingPhaseInfo).To(Equal(mcdp.calledPhases[0]))
Expect(ProcessingPhaseTransferDataDir).To(Equal(mcdp.calledPhases[1]))
Expect("dataDir").To(Equal(mcdp.transferPath))
Expect(ProcessingPhaseFoo).To(Equal(mcdp.calledPhases[2]))
})
It("should return error if there is a loop", func() {
mcdp := &MockCustomizedDataProvider{
MockDataProvider: MockDataProvider{
infoResponse: ProcessingPhaseTransferDataDir,
transferResponse: ProcessingPhaseFoo,
},
fooResponse: ProcessingPhaseInfo,
}
dp := NewDataProcessor(mcdp, "dest", "dataDir", "scratchDataDir", "1G", 0.055, false)
dp.RegisterPhaseExecutor(ProcessingPhaseFoo, func() (ProcessingPhase, error) {
return mcdp.Foo()
})
err := dp.ProcessData()
Expect(err).To(HaveOccurred())
})
It("should return error if there is an unknown phase", func() {
mdp := &MockDataProvider{
infoResponse: "unknown",
}
dp := NewDataProcessor(mdp, "dest", "dataDir", "scratchDataDir", "1G", 0.055, false)
err := dp.ProcessData()
Expect(err).To(HaveOccurred())
})
table.DescribeTable("should avoid cleanup before delta copies", func(dataSource DataSourceInterface, expectedCleanup bool) {
tmpDir, err := os.MkdirTemp("", "scratch")
Expect(err).ToNot(HaveOccurred())
defer os.RemoveAll(tmpDir)
dp := NewDataProcessor(dataSource, "dest", "dataDir", tmpDir, "1G", 0.055, false)
Expect(dp.needsDataCleanup).To(Equal(expectedCleanup))
},
table.Entry("ImageIO delta copy", &ImageioDataSource{currentSnapshot: "123", previousSnapshot: "123"}, false),
table.Entry("ImageIO base copy", &ImageioDataSource{currentSnapshot: "123", previousSnapshot: ""}, true),
table.Entry("VDDK delta copy", &VDDKDataSource{CurrentSnapshot: "123", PreviousSnapshot: "123"}, false),
table.Entry("VDDK base copy", &VDDKDataSource{CurrentSnapshot: "123", PreviousSnapshot: ""}, true),
)
})
var _ = Describe("Convert", func() {
It("Should successfully convert and return resize", func() {
url, err := url.Parse("http://fakeurl-notreal.fake")
Expect(err).ToNot(HaveOccurred())
mdp := &MockDataProvider{
url: url,
}
dp := NewDataProcessor(mdp, "dest", "dataDir", "scratchDataDir", "1G", 0.055, false)
qemuOperations := NewFakeQEMUOperations(nil, nil, fakeInfoOpRetVal{&fakeZeroImageInfo, errors.New("Scratch space required, and none found ")}, nil, nil, nil)
replaceQEMUOperations(qemuOperations, func() {
nextPhase, err := dp.convert(mdp.GetURL())
Expect(err).ToNot(HaveOccurred())
Expect(ProcessingPhaseResize).To(Equal(nextPhase))
})
})
It("Should fail when validation fails and return Error", func() {
url, err := url.Parse("http://fakeurl-notreal.fake")
Expect(err).ToNot(HaveOccurred())
mdp := &MockDataProvider{
url: url,
}
dp := NewDataProcessor(mdp, "dest", "dataDir", "scratchDataDir", "1G", 0.055, false)
qemuOperations := NewFakeQEMUOperations(nil, nil, fakeInfoOpRetVal{&fakeZeroImageInfo, errors.New("Scratch space required, and none found ")}, errors.New("Validation failure"), nil, nil)
replaceQEMUOperations(qemuOperations, func() {
nextPhase, err := dp.convert(mdp.GetURL())
Expect(err).To(HaveOccurred())
Expect(ProcessingPhaseError).To(Equal(nextPhase))
})
})
It("Should fail when conversion fails and return Error", func() {
url, err := url.Parse("http://fakeurl-notreal.fake")
Expect(err).ToNot(HaveOccurred())
mdp := &MockDataProvider{
url: url,
}
dp := NewDataProcessor(mdp, "dest", "dataDir", "scratchDataDir", "1G", 0.055, false)
qemuOperations := NewFakeQEMUOperations(errors.New("Conversion failure"), nil, fakeInfoOpRetVal{&fakeZeroImageInfo, errors.New("Scratch space required, and none found ")}, nil, nil, nil)
replaceQEMUOperations(qemuOperations, func() {
nextPhase, err := dp.convert(mdp.GetURL())
Expect(err).To(HaveOccurred())
Expect(ProcessingPhaseError).To(Equal(nextPhase))
})
})
})
var _ = Describe("Resize", func() {
It("Should not resize and return complete, when requestedSize is blank", func() {
tempDir, err := os.MkdirTemp(os.TempDir(), "dest")
Expect(err).ToNot(HaveOccurred())
url, err := url.Parse("http://fakeurl-notreal.fake")
Expect(err).ToNot(HaveOccurred())
mdp := &MockDataProvider{
url: url,
}
dp := NewDataProcessor(mdp, tempDir, "dataDir", "scratchDataDir", "", 0.055, false)
qemuOperations := NewFakeQEMUOperations(nil, nil, fakeInfoOpRetVal{&fakeZeroImageInfo, nil}, nil, nil, nil)
replaceQEMUOperations(qemuOperations, func() {
nextPhase, err := dp.resize()
Expect(err).ToNot(HaveOccurred())
Expect(ProcessingPhaseComplete).To(Equal(nextPhase))
})
})
It("Should not resize and return complete, when requestedSize is valid, but datadir doesn't exist (block device)", func() {
tempDir, err := os.MkdirTemp(os.TempDir(), "dest")
Expect(err).ToNot(HaveOccurred())
replaceAvailableSpaceBlockFunc(func(dataDir string) (int64, error) {
Expect(tempDir).To(Equal(dataDir))
return int64(100000), nil
}, func() {
url, err := url.Parse("http://fakeurl-notreal.fake")
Expect(err).ToNot(HaveOccurred())
mdp := &MockDataProvider{
url: url,
}
dp := NewDataProcessor(mdp, tempDir, "dataDir", "scratchDataDir", "1G", 0.055, false)
qemuOperations := NewFakeQEMUOperations(nil, nil, fakeInfoOpRetVal{&fakeZeroImageInfo, nil}, nil, nil, nil)
replaceQEMUOperations(qemuOperations, func() {
nextPhase, err := dp.resize()
Expect(err).ToNot(HaveOccurred())
Expect(ProcessingPhaseComplete).To(Equal(nextPhase))
})
})
})
It("Should resize and return complete, when requestedSize is valid, and datadir exists", func() {
tmpDir, err := os.MkdirTemp(os.TempDir(), "data")
Expect(err).ToNot(HaveOccurred())
url, err := url.Parse("http://fakeurl-notreal.fake")
Expect(err).ToNot(HaveOccurred())
mdp := &MockDataProvider{
url: url,
}
dp := NewDataProcessor(mdp, tmpDir, tmpDir, "scratchDataDir", "1G", 0.055, false)
qemuOperations := NewFakeQEMUOperations(nil, nil, fakeInfoOpRetVal{&fakeZeroImageInfo, nil}, nil, nil, nil)
replaceQEMUOperations(qemuOperations, func() {
nextPhase, err := dp.resize()
Expect(err).ToNot(HaveOccurred())
Expect(ProcessingPhaseComplete).To(Equal(nextPhase))
})
})
It("Should not resize and return error, when ResizeImage fails", func() {
tmpDir, err := os.MkdirTemp(os.TempDir(), "data")
Expect(err).ToNot(HaveOccurred())
url, err := url.Parse("http://fakeurl-notreal.fake")
Expect(err).ToNot(HaveOccurred())
mdp := &MockDataProvider{
url: url,
}
dp := NewDataProcessor(mdp, "dest", tmpDir, "scratchDataDir", "1G", 0.055, false)
qemuOperations := NewQEMUAllErrors()
replaceQEMUOperations(qemuOperations, func() {
nextPhase, err := dp.resize()
Expect(err).To(HaveOccurred())
Expect(ProcessingPhaseError).To(Equal(nextPhase))
})
})
It("Should return same value as replaced function", func() {
replaceAvailableSpaceBlockFunc(func(dataDir string) (int64, error) {
return int64(100000), nil
}, func() {
mdp := &MockDataProvider{}
dp := NewDataProcessor(mdp, "dest", "dataDir", "scratchDataDir", "", 0.055, false)
Expect(int64(100000)).To(Equal(dp.calculateTargetSize()))
})
})
It("Should fail if calculate size returns failure", func() {
replaceAvailableSpaceBlockFunc(func(dataDir string) (int64, error) {
return int64(-1), errors.New("error")
}, func() {
mdp := &MockDataProvider{}
dp := NewDataProcessor(mdp, "dest", "dataDir", "scratchDataDir", "", 0.055, false)
// We just log the error if one happens.
Expect(int64(-1)).To(Equal(dp.calculateTargetSize()))
})
})
})
var _ = Describe("ResizeImage", func() {
//fakeInfoRet has info.VirtualSize=1024
table.DescribeTable("calling ResizeImage", func(qemuOperations image.QEMUOperations, imageSize string, totalSpace int64, wantErr bool) {
replaceQEMUOperations(qemuOperations, func() {
err := ResizeImage("dest", imageSize, totalSpace, false)
if !wantErr {
Expect(err).ToNot(HaveOccurred())
} else {
Expect(err).To(HaveOccurred())
}
})
},
table.Entry("successfully resize to imageSize when imageSize > info.VirtualSize and < totalSize", NewFakeQEMUOperations(nil, nil, fakeInfoRet, nil, nil, resource.NewScaledQuantity(int64(1500*1024), 0)), "1536000", int64(2048*1024), false),
table.Entry("successfully resize to totalSize when imageSize > info.VirtualSize and > totalSize", NewFakeQEMUOperations(nil, nil, fakeInfoRet, nil, nil, resource.NewScaledQuantity(int64(2048*1024), 0)), "2560000", int64(2048*1024), false),
table.Entry("successfully do nothing when imageSize = info.VirtualSize and > totalSize", NewFakeQEMUOperations(nil, nil, fakeInfoRet, nil, nil, resource.NewScaledQuantity(int64(1024*1024), 0)), "1048576", int64(1024*1024), false),
table.Entry("fail to resize to with blank imageSize", NewFakeQEMUOperations(nil, nil, fakeInfoRet, nil, nil, resource.NewScaledQuantity(int64(2048), 0)), "", int64(2048), true),
table.Entry("fail to resize to with blank imageSize", NewQEMUAllErrors(), "", int64(2048), true),
)
})
var _ = Describe("DataProcessorResume", func() {
It("Should fail with an error if the data provider cannot resume", func() {
mdp := &MockDataProvider{}
dp := NewDataProcessor(mdp, "dest", "dataDir", "scratchDataDir", "", 0.055, false)
err := dp.ProcessDataResume()
Expect(err).To(HaveOccurred())
})
It("Should resume properly based on resume phase", func() {
amdp := &MockAsyncDataProvider{
ResumePhase: ProcessingPhaseComplete,
}
dp := NewDataProcessor(amdp, "dest", "dataDir", "scratchDataDir", "", 0.055, false)
err := dp.ProcessDataResume()
Expect(err).ToNot(HaveOccurred())
})
})
var _ = Describe("MergeDelta", func() {
It("Should correctly move to merge phase, then rebase and commit", func() {
url := &url.URL{}
originalBackingFile := "original-backing-file"
expectedBackingFile := "rebased-backing-file"
originalActualSize := int64(5)
expectedActualSize := int64(6)
mdp := &MockDataProvider{
infoResponse: ProcessingPhaseTransferScratch,
transferResponse: ProcessingPhaseMergeDelta,
needsScratch: true,
url: url,
}
dp := NewDataProcessor(mdp, expectedBackingFile, "dataDir", "scratchDataDir", "", 0.055, false)
err := errors.New("this operation should not be called")
info := &image.ImgInfo{
Format: "",
BackingFile: originalBackingFile,
VirtualSize: 10,
ActualSize: originalActualSize,
}
qemuOperations := NewFakeQEMUOperations(err, err, fakeInfoOpRetVal{info, nil}, err, err, nil)
replaceQEMUOperations(qemuOperations, func() {
// Check original backing file and size before processing
info, err := qemuOperations.Info(url)
Expect(err).ToNot(HaveOccurred())
Expect(info.BackingFile).To(Equal(originalBackingFile))
Expect(info.ActualSize).To(Equal(originalActualSize))
// This should run rebase and commit
err = dp.ProcessData()
Expect(err).ToNot(HaveOccurred())
Expect(2).To(Equal(len(mdp.calledPhases)))
Expect(ProcessingPhaseInfo).To(Equal(mdp.calledPhases[0]))
Expect(ProcessingPhaseTransferScratch).To(Equal(mdp.calledPhases[1]))
// Verify backing file was rebased and committed to main data file
info, err = qemuOperations.Info(url)
Expect(err).ToNot(HaveOccurred())
Expect(info.BackingFile).To(Equal(expectedBackingFile))
Expect(info.ActualSize).To(Equal(expectedActualSize))
})
})
})
func replaceQEMUOperations(replacement image.QEMUOperations, f func()) {
orig := qemuOperations
if replacement != nil {
qemuOperations = replacement
defer func() { qemuOperations = orig }()
}
f()
}
func NewFakeQEMUOperations(e2, e3 error, ret4 fakeInfoOpRetVal, e5 error, e6 error, targetResize *resource.Quantity) image.QEMUOperations {
return &fakeQEMUOperations{e2, e3, ret4, e5, e6, targetResize}
}
func (o *fakeQEMUOperations) ConvertToRawStream(*url.URL, string, bool) error {
return o.e2
}
func (o *fakeQEMUOperations) Validate(*url.URL, int64) error {
return o.e5
}
func (o *fakeQEMUOperations) Resize(dest string, size resource.Quantity, preallocate bool) error {
if o.resizeQuantity != nil {
Expect(o.resizeQuantity.Cmp(size)).To(Equal(0), "sizes don't match %v, %v", o.resizeQuantity.String(), size.String())
}
return o.e3
}
func (o *fakeQEMUOperations) Info(url *url.URL) (*image.ImgInfo, error) {
return o.ret4.imgInfo, o.ret4.e
}
func (o *fakeQEMUOperations) CreateBlankImage(dest string, size resource.Quantity, preallocate bool) error {
return o.e6
}
// Simulate rebase by changing the backing file.
func (o *fakeQEMUOperations) Rebase(backingFile string, delta string) error {
if o.ret4.imgInfo == nil {
return errors.New("invalid image info")
}
o.ret4.imgInfo.BackingFile = backingFile
return nil
}
// Simulate commit by increasing the image size.
func (o *fakeQEMUOperations) Commit(image string) error {
if o.ret4.imgInfo == nil {
return errors.New("invalid image info")
}
o.ret4.imgInfo.ActualSize++
return nil
}
func NewQEMUAllErrors() image.QEMUOperations {
err := errors.New("qemu should not be called from this test override with replaceQEMUOperations")
return NewFakeQEMUOperations(err, err, fakeInfoOpRetVal{nil, err}, err, err, nil)
}
func replaceAvailableSpaceBlockFunc(replacement func(string) (int64, error), f func()) {
origFunc := getAvailableSpaceBlockFunc
getAvailableSpaceBlockFunc = replacement
defer func() {
getAvailableSpaceBlockFunc = origFunc
}()
f()
}
func replaceAvailableSpaceFunc(replacement func(string) (int64, error), f func()) {
origFunc := getAvailableSpaceFunc
getAvailableSpaceFunc = replacement
defer func() {
getAvailableSpaceFunc = origFunc
}()
f()
}