forked from kubevirt/containerized-data-importer
/
fileConversion.go
194 lines (165 loc) · 5.32 KB
/
fileConversion.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
package utils
import (
"archive/tar"
"compress/gzip"
"io"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/pkg/errors"
"github.com/ulikunitz/xz"
"kubevirt.io/containerized-data-importer/pkg/image"
)
var formatTable = map[string]func(string, string) (string, error){
image.ExtGz: toGz,
image.ExtXz: toXz,
image.ExtTar: toTar,
image.ExtQcow2: toQcow2,
"": toNoop,
}
// FormatTestData accepts the path of a single file (srcFile) and attempts to generate an output
// file in the format defined by targetFormats (e.g. ".tar", ".gz" will produce a .tar.gz formatted file). The output file is written to the directory in `tgtDir`.
// returns:
// (string) Path of output file
// (error) Errors that occur during formatting
func FormatTestData(srcFile, tgtDir string, targetFormats ...string) (string, error) {
var err error
for _, tf := range targetFormats {
f, ok := formatTable[tf]
if !ok {
return "", errors.Errorf("format extension %q not recognized", tf)
}
// invoke conversion func
srcFile, err = f(srcFile, tgtDir)
if err != nil {
return "", errors.Wrap(err, "could not format test data")
}
}
return srcFile, nil
}
func toTar(src, tgtDir string) (string, error) {
return ArchiveFiles(src, tgtDir, src)
}
// ArchiveFiles creates a tar file that archives the given source files.
func ArchiveFiles(targetFile, tgtDir string, sourceFilesNames ...string) (string, error) {
tgtFile, tgtPath, _ := createTargetFile(targetFile, tgtDir, image.ExtTar)
defer tgtFile.Close()
w := tar.NewWriter(tgtFile)
defer w.Close()
for _, src := range sourceFilesNames {
srcFile, err := os.Open(src)
if err != nil {
return "", errors.Wrapf(err, "Error opening file %s", src)
}
defer srcFile.Close()
srcFileInfo, err := srcFile.Stat()
if err != nil {
return "", errors.Wrapf(err, "Error stating file %s", src)
}
hdr, err := tar.FileInfoHeader(srcFileInfo, "")
if err != nil {
return "", errors.Wrapf(err, "Error generating tar file header for %s", src)
}
err = w.WriteHeader(hdr)
if err != nil {
return "", errors.Wrapf(err, "Error writing tar header to %s", tgtPath)
}
_, err = io.Copy(w, srcFile)
if err != nil {
return "", errors.Wrapf(err, "Error writing to file %s", tgtPath)
}
}
return tgtPath, nil
}
func toGz(src, tgtDir string) (string, error) {
tgtFile, tgtPath, _ := createTargetFile(src, tgtDir, image.ExtGz)
defer tgtFile.Close()
w := gzip.NewWriter(tgtFile)
defer w.Close()
srcFile, err := os.Open(src)
if err != nil {
return "", errors.Wrapf(err, "Error opening file %s", src)
}
defer srcFile.Close()
_, err = io.Copy(w, srcFile)
if err != nil {
return "", errors.Wrapf(err, "Error writing to file %s", tgtPath)
}
return tgtPath, nil
}
func toXz(src, tgtDir string) (string, error) {
tgtFile, tgtPath, _ := createTargetFile(src, tgtDir, image.ExtXz)
defer tgtFile.Close()
w, err := xz.NewWriter(tgtFile)
if err != nil {
return "", errors.Wrapf(err, "Error getting xz writer for file %s", tgtPath)
}
defer w.Close()
srcFile, err := os.Open(src)
if err != nil {
return "", errors.Wrapf(err, "Error opening file %s", src)
}
defer srcFile.Close()
_, err = io.Copy(w, srcFile)
if err != nil {
return "", errors.Wrapf(err, "Error writing to file %s", tgtPath)
}
return tgtPath, nil
}
func toQcow2(srcfile, tgtDir string) (string, error) {
base := strings.TrimSuffix(filepath.Base(srcfile), ".iso")
tgt := filepath.Join(tgtDir, base+image.ExtQcow2)
args := []string{"convert", "-f", "raw", "-O", "qcow2", srcfile, tgt}
if err := doCmdAndVerifyFile(tgt, "qemu-img", args...); err != nil {
return "", err
}
return tgt, nil
}
func toNoop(src, tgtDir string) (string, error) {
return copyIfNotPresent(src, tgtDir)
}
func doCmdAndVerifyFile(tgt, cmd string, args ...string) error {
if err := doCmd(cmd, args...); err != nil {
return err
}
if _, err := os.Stat(tgt); err != nil {
return errors.Wrapf(err, "Failed to stat file %q", tgt)
}
return nil
}
func doCmd(osCmd string, osArgs ...string) error {
cmd := exec.Command(osCmd, osArgs...)
cout, err := cmd.CombinedOutput()
if err != nil {
return errors.Wrapf(err, "OS command `%s %v` errored: %v\nStdout/Stderr: %s", osCmd, strings.Join(osArgs, " "), err, string(cout))
}
return nil
}
// copyIfNotPresent checks for the src file in the tgtDir. If it is not there, it attempts to copy it from src to tgtdir.
// If a copy is performed, the path to the copy is returned.
// If the file already exists, the original src string is returned.
func copyIfNotPresent(src, tgtDir string) (string, error) {
ret := filepath.Join(tgtDir, filepath.Base(src))
_, err := os.Stat(ret)
if err != nil && !os.IsNotExist(err) {
return "", errors.Wrap(err, "Unexpected error stating file")
}
if os.IsNotExist(err) {
if err = doCmd("cp", src, ret); err != nil {
return "", err
}
}
return ret, nil
}
// createTargetFile is a simple helper to create a file with the provided extension in the target directory.
// returns a pointer to the new file, path to the new file, or an error. It is the responsibility of the caller to
// close the file.
func createTargetFile(src, tgtDir, ext string) (*os.File, string, error) {
tgt := filepath.Join(tgtDir, filepath.Base(src)+ext)
tgtFile, err := os.Create(tgt)
if err != nil {
return nil, "", errors.Wrap(err, "Error creating file")
}
return tgtFile, tgt, nil
}