Skip to content

Commit 888f532

Browse files
authored
Merge branch 'main' into add-lua-filter
2 parents 6b24d0e + 81ebe93 commit 888f532

File tree

3 files changed

+248
-7
lines changed

3 files changed

+248
-7
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6060
- Fix building on windows [#115](https://github.com/AdRoll/baker/issues/115)
6161
- Fix `list_test` with file URI to be compatible with windows paths [#117](https://github.com/AdRoll/baker/pull/117)
6262
- Fix `List` input, some `io.Reader`'s were left opened [#118](https://github.com/AdRoll/baker/pull/118)
63-
- `split_writer.Close` leaves some file descriptor open [#119](https://github.com/AdRoll/baker/pull/119)
6463
- Fix some bugs in `s3.s3UploadFile` [#120](https://github.com/AdRoll/baker/pull/120)
64+
- `SplitWriter` leaves some file descriptors open [#119](https://github.com/AdRoll/baker/pull/119) and [#121](https://github.com/AdRoll/baker/pull/121)
6565

6666
### Maintenance
6767

pkg/splitwriter/split_writer.go

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -191,23 +191,31 @@ func doNextSplit(f *os.File, dir, next string, off int64) (*os.File, error) {
191191
nextpath := filepath.Join(dir, next)
192192
nextf, err := open(nextpath)
193193
if err != nil {
194+
closeFiles(f)
194195
return nil, err
195196
}
196197

197198
if _, err := f.Seek(off, io.SeekStart); err != nil {
199+
closeFiles(f, nextf)
198200
return nil, err
199201
}
200202

201203
if _, err := io.Copy(nextf, f); err != nil {
204+
closeFiles(f, nextf)
202205
return nil, err
203206
}
204207

205208
// Conclude the current split
206209
if err := f.Truncate(off); err != nil {
210+
closeFiles(f, nextf)
207211
return nil, err
208212
}
209213

210-
return nextf, f.Close()
214+
if err := f.Close(); err != nil {
215+
closeFiles(nextf)
216+
return nil, err
217+
}
218+
return nextf, nil
211219
}
212220

213221
// doFirstSplit creates the first splits of f, at offset off.
@@ -222,36 +230,45 @@ func doFirstSplit(f *os.File, dir, fname, next1 string, off int64) (*os.File, er
222230
// Open the 2 first parts
223231
f1, err := open(next1path)
224232
if err != nil {
233+
closeFiles(f)
225234
return nil, err
226235
}
227236
f2, err := open(next2path)
228237
if err != nil {
238+
closeFiles(f, f1)
229239
return nil, err
230240
}
231241

232242
// Split the original file into 2 parts
233243
if _, err := f.Seek(0, io.SeekStart); err != nil {
244+
closeFiles(f, f1, f2)
234245
return nil, err
235246
}
236247

237248
if _, err := io.CopyN(f1, f, off); err != nil {
249+
closeFiles(f, f1, f2)
238250
return nil, err
239251
}
240252

241253
if _, err := io.Copy(f2, f); err != nil {
254+
closeFiles(f, f1, f2)
242255
return nil, err
243256
}
244257

245258
// Close the 2 parts and remove the original file
246259
if err := f.Close(); err != nil {
260+
closeFiles(f1, f2)
247261
return nil, err
248262
}
249-
250263
if err := f1.Close(); err != nil {
264+
closeFiles(f2)
251265
return nil, err
252266
}
253-
254-
return f2, os.Remove(filepath.Join(dir, fname))
267+
if err := os.Remove(filepath.Join(dir, fname)); err != nil {
268+
closeFiles(f2)
269+
return nil, err
270+
}
271+
return f2, nil
255272
}
256273

257274
var splitFnameRx = regexp.MustCompile(`(\S+)-part-(\d+)(.*)`)
@@ -298,3 +315,13 @@ func fileExists(fname string) bool {
298315
}
299316
return !info.IsDir()
300317
}
318+
319+
// closeFiles closes a list of files and returns the first error (if any).
320+
func closeFiles(fs ...*os.File) (cerr error) {
321+
for _, f := range fs {
322+
if err := f.Close(); err != nil && cerr == nil {
323+
cerr = err
324+
}
325+
}
326+
return
327+
}

pkg/splitwriter/split_writer_test.go

Lines changed: 216 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
package splitwriter
22

33
import (
4+
"fmt"
5+
"io"
46
"io/ioutil"
57
"os"
68
"path"
9+
"path/filepath"
10+
"runtime"
11+
"strings"
712
"testing"
813
)
914

@@ -178,12 +183,12 @@ func TestSplitWriter(t *testing.T) {
178183
for _, tt := range tests {
179184
t.Run(tt.name, func(t *testing.T) {
180185
for f, data := range tt.previous {
181-
if err := ioutil.WriteFile(path.Join(dir, f), []byte(data), 0666); err != nil {
186+
if err := ioutil.WriteFile(filepath.Join(dir, f), []byte(data), 0666); err != nil {
182187
t.Fatal(err)
183188
}
184189
}
185190

186-
path := path.Join(dir, tt.fname)
191+
path := filepath.Join(dir, tt.fname)
187192
w, err := New(path, int64(tt.maxsize), int64(tt.bufsize))
188193
if err != nil {
189194
t.Fatal(err)
@@ -201,6 +206,215 @@ func TestSplitWriter(t *testing.T) {
201206
}
202207
}
203208

209+
func TestSplitWriterErr(t *testing.T) {
210+
type swConf struct {
211+
path string // path to file
212+
maxsize, bufsize int // splitWriter parameters
213+
data string // data to write in fname
214+
}
215+
initSplitWriter := func(t *testing.T, cf swConf) io.WriteCloser {
216+
w, err := New(cf.path, int64(cf.maxsize), int64(cf.bufsize))
217+
if err != nil {
218+
t.Fatal(err)
219+
}
220+
if _, err := w.Write([]byte(cf.data)); err != nil {
221+
t.Fatal(err)
222+
}
223+
return w
224+
}
225+
226+
t.Run("doFirstSplit f1 error ", func(t *testing.T) {
227+
dir := t.TempDir()
228+
229+
cfg := swConf{
230+
path: filepath.Join(dir, "big-file-1-split"),
231+
maxsize: 12,
232+
bufsize: 4,
233+
data: "012\n34567890123",
234+
}
235+
w := initSplitWriter(t, cfg)
236+
237+
// Create the first part whitout write permission
238+
f1, _ := os.OpenFile(cfg.path+"-part-1", os.O_CREATE, 0444)
239+
f1.Close()
240+
241+
if err := w.Close(); err == nil {
242+
t.Errorf("get nil, want error")
243+
}
244+
245+
// Check that all files where closed
246+
if err := os.RemoveAll(dir); err != nil {
247+
t.Fatal(err)
248+
}
249+
})
250+
251+
t.Run("doFirstSplit f2 err", func(t *testing.T) {
252+
dir := t.TempDir()
253+
254+
cfg := swConf{
255+
path: filepath.Join(dir, "big-file-1-split"),
256+
maxsize: 12,
257+
bufsize: 4,
258+
data: "012\n34567890123",
259+
}
260+
w := initSplitWriter(t, cfg)
261+
262+
// Create the second part whitout write permission
263+
f2, _ := os.OpenFile(cfg.path+"-part-2", os.O_CREATE, 0444)
264+
f2.Close()
265+
266+
if err := w.Close(); err == nil {
267+
t.Errorf("get nil, want error")
268+
}
269+
270+
// Check that all files where closed
271+
if err := os.RemoveAll(dir); err != nil {
272+
t.Fatal(err)
273+
}
274+
})
275+
276+
t.Run("doFirstSplit error on remove", func(t *testing.T) {
277+
if "windows" != runtime.GOOS {
278+
t.Skip("Cannot produce an error on file remove on a not windows platform")
279+
}
280+
281+
dir := t.TempDir()
282+
283+
cfg := swConf{
284+
path: filepath.Join(dir, "big-file-1-split"),
285+
maxsize: 12,
286+
bufsize: 4,
287+
data: "012\n34567890123",
288+
}
289+
w := initSplitWriter(t, cfg)
290+
291+
// Open the log file to prevent cancellation during w.Close()
292+
f, _ := os.OpenFile(cfg.path, os.O_CREATE|os.O_RDWR, 0)
293+
294+
if err := w.Close(); err == nil {
295+
t.Errorf("get nil, want error")
296+
}
297+
298+
// Check that all files where closed
299+
f.Close()
300+
if err := os.RemoveAll(dir); err != nil {
301+
t.Fatal(err)
302+
}
303+
})
304+
305+
t.Run("doNextSplit f error", func(t *testing.T) {
306+
dir := t.TempDir()
307+
308+
cfg := swConf{
309+
path: filepath.Join(dir, "big-file-ext-2-split"),
310+
maxsize: 6,
311+
bufsize: 4,
312+
data: "012\n3456\n7890123",
313+
}
314+
w := initSplitWriter(t, cfg)
315+
316+
// Create the third part whitout write permission
317+
f2, _ := os.OpenFile(cfg.path+"-part-3", os.O_CREATE, 0444)
318+
f2.Close()
319+
320+
if err := w.Close(); err == nil {
321+
t.Errorf("get nil, want error")
322+
}
323+
324+
// Check that all files where closed
325+
if err := os.RemoveAll(dir); err != nil {
326+
t.Fatal(err)
327+
}
328+
})
329+
330+
}
331+
332+
func TestCloseFiles(t *testing.T) {
333+
tests := []struct {
334+
name string // test name
335+
fnames []string // fname to create
336+
close []int // indeces of file to close
337+
}{
338+
{
339+
name: "good 1",
340+
fnames: []string{"test0"},
341+
close: []int{}, // want no error
342+
},
343+
{
344+
name: "good 2",
345+
fnames: []string{"test0", "test1"},
346+
},
347+
{
348+
name: "good 3",
349+
fnames: []string{"test0", "test1", "test2"},
350+
},
351+
{
352+
name: "bad 1",
353+
fnames: []string{"test0", "test1", "test2"},
354+
close: []int{0}, // want error
355+
},
356+
{
357+
name: "bad 2",
358+
fnames: []string{"test0", "test1", "test2"},
359+
close: []int{1},
360+
},
361+
{
362+
name: "bad 3",
363+
fnames: []string{"test0", "test1", "test2"},
364+
close: []int{2},
365+
},
366+
{
367+
name: "bad 1,3",
368+
fnames: []string{"test0", "test1", "test2"},
369+
close: []int{1, 2},
370+
},
371+
}
372+
for _, tt := range tests {
373+
t.Run(tt.name, func(t *testing.T) {
374+
dir := t.TempDir()
375+
376+
files := make([]*os.File, 0, 3)
377+
for _, fname := range tt.fnames {
378+
f, _ := os.OpenFile(filepath.Join(dir, fname), os.O_CREATE|os.O_RDWR, 0)
379+
files = append(files, f)
380+
}
381+
382+
for _, i := range tt.close {
383+
files[i].Close()
384+
}
385+
386+
err := closeFiles(files...)
387+
388+
// Check returned error
389+
if len(tt.close) == 0 {
390+
// Want no error
391+
if err != nil {
392+
t.Errorf("get %v, want nil", err)
393+
}
394+
} else {
395+
// Want error
396+
if err == nil {
397+
t.Errorf("get nil, want error")
398+
} else {
399+
// Check we return the first error
400+
fname := fmt.Sprintf("test%d", tt.close[0])
401+
if !strings.Contains(err.Error(), fname) {
402+
t.Errorf("error %v not contains %q", err, fname)
403+
}
404+
}
405+
}
406+
407+
// Check that all files were closed
408+
for _, f := range files {
409+
err := f.Close()
410+
if err == nil {
411+
t.Errorf("file %q not close", f.Name())
412+
}
413+
}
414+
})
415+
}
416+
}
417+
204418
func Test_nextSplit(t *testing.T) {
205419
tests := []struct {
206420
fname string

0 commit comments

Comments
 (0)