Skip to content

Commit c22f991

Browse files
authored
Merge pull request #64 from semaphoreci/rl/fix-too-many-open-files
Fix too many open files error
2 parents fc9ccc3 + 7d1b2f7 commit c22f991

File tree

6 files changed

+147
-30
lines changed

6 files changed

+147
-30
lines changed

cmd/compile.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ limitations under the License.
1818

1919
import (
2020
"encoding/json"
21-
"io/ioutil"
2221
"os"
2322

2423
"github.com/semaphoreci/test-results/pkg/cli"
@@ -51,12 +50,14 @@ var compileCmd = &cobra.Command{
5150
return err
5251
}
5352

54-
dirPath, err := ioutil.TempDir("", "test-results-*")
53+
dirPath, err := os.MkdirTemp("", "test-results-*")
5554

5655
if err != nil {
5756
return err
5857
}
5958

59+
defer os.RemoveAll(dirPath)
60+
6061
for _, path := range paths {
6162
parser, err := cli.FindParser(path, cmd)
6263
if err != nil {
@@ -73,7 +74,7 @@ var compileCmd = &cobra.Command{
7374
return err
7475
}
7576

76-
tmpFile, err := ioutil.TempFile(dirPath, "result-*.json")
77+
tmpFile, err := os.CreateTemp(dirPath, "result-*.json")
7778
if err != nil {
7879
return err
7980
}
@@ -100,8 +101,6 @@ var compileCmd = &cobra.Command{
100101
return err
101102
}
102103

103-
defer os.RemoveAll(dirPath)
104-
105104
return nil
106105
},
107106
}

cmd/gen-pipeline-report.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ limitations under the License.
1818

1919
import (
2020
"encoding/json"
21-
"io/ioutil"
2221
"os"
2322
"path"
2423

@@ -53,7 +52,7 @@ var genPipelineReportCmd = &cobra.Command{
5352
}
5453

5554
if len(args) == 0 {
56-
dir, err = ioutil.TempDir("", "test-results")
55+
dir, err = os.MkdirTemp("", "test-results")
5756
if err != nil {
5857
logger.Error("Creating temporary directory failed %v", err)
5958
return err

cmd/publish.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ limitations under the License.
1919
import (
2020
"encoding/json"
2121
"fmt"
22-
"io/ioutil"
2322
"os"
2423
"path"
2524

@@ -50,12 +49,14 @@ var publishCmd = &cobra.Command{
5049
return err
5150
}
5251

53-
dirPath, err := ioutil.TempDir("", "test-results-*")
52+
dirPath, err := os.MkdirTemp("", "test-results-*")
5453

5554
if err != nil {
5655
return err
5756
}
5857

58+
defer os.RemoveAll(dirPath)
59+
5960
for _, path := range paths {
6061
parser, err := cli.FindParser(path, cmd)
6162
if err != nil {
@@ -72,7 +73,7 @@ var publishCmd = &cobra.Command{
7273
return err
7374
}
7475

75-
tmpFile, err := ioutil.TempFile(dirPath, "result-*.json")
76+
tmpFile, err := os.CreateTemp(dirPath, "result-*.json")
7677
if err != nil {
7778
return err
7879
}
@@ -99,6 +100,8 @@ var publishCmd = &cobra.Command{
99100
return err
100101
}
101102

103+
defer os.Remove(fileName)
104+
102105
_, err = cli.PushArtifacts("job", fileName, path.Join("test-results", "junit.json"), cmd)
103106
if err != nil {
104107
return err
@@ -150,8 +153,6 @@ var publishCmd = &cobra.Command{
150153
}
151154
}
152155

153-
defer os.Remove(fileName)
154-
155156
return nil
156157
},
157158
}

pkg/cli/cli.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ package cli
33
import (
44
"encoding/json"
55
"fmt"
6+
"io"
67
"io/fs"
7-
"io/ioutil"
88
"os"
99
"os/exec"
1010
"path/filepath"
@@ -196,17 +196,20 @@ func Marshal(testResults parser.Result) ([]byte, error) {
196196
// WriteToFile saves data to given file
197197
func WriteToFile(data []byte, path string) (string, error) {
198198
file, err := os.Create(path) // #nosec
199+
defer file.Close()
199200

200201
if err != nil {
201202
logger.Error("Opening file %s: %v", path, err)
202203
return "", err
203204
}
205+
204206
return writeToFile(data, file)
205207
}
206208

207209
// WriteToTmpFile saves data to temporary file
208210
func WriteToTmpFile(data []byte) (string, error) {
209-
file, err := ioutil.TempFile("", "test-results")
211+
file, err := os.CreateTemp("", "test-results")
212+
defer file.Close()
210213

211214
if err != nil {
212215
logger.Error("Opening file %s: %v", file.Name(), err)
@@ -225,6 +228,11 @@ func writeToFile(data []byte, file *os.File) (string, error) {
225228
return "", err
226229
}
227230

231+
if err = file.Sync(); err != nil {
232+
logger.Error("Output file write failed: %v", err)
233+
return "", err
234+
}
235+
228236
return file.Name(), nil
229237
}
230238

@@ -385,10 +393,8 @@ func Load(path string) (*parser.Result, error) {
385393
return nil, err
386394
}
387395
defer jsonFile.Close() // #nosec
388-
if err != nil {
389-
return nil, err
390-
}
391-
bytes, err := ioutil.ReadAll(jsonFile)
396+
397+
bytes, err := io.ReadAll(jsonFile)
392398
if err != nil {
393399
return nil, err
394400
}

pkg/cli/cli_test.go

Lines changed: 117 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package cli_test
22

33
import (
4+
"encoding/json"
45
"fmt"
5-
"io/ioutil"
6+
"github.com/semaphoreci/test-results/pkg/parser"
7+
"github.com/stretchr/testify/require"
68
"os"
79
"testing"
810

@@ -41,10 +43,22 @@ func Test_LoadFiles(t *testing.T) {
4143

4244
os.RemoveAll(dirPath)
4345
})
46+
47+
t.Run("with big directory", func(t *testing.T) {
48+
dirPath := generateDirWithFilesAndNestedDir(t, 2600, 3)
49+
assert.NotEmpty(t, dirPath)
50+
51+
paths, err := cli.LoadFiles([]string{dirPath}, ".xml")
52+
assert.Len(t, paths, 2600, "should return correct number of files")
53+
assert.Nil(t, err, "should not throw error")
54+
55+
os.RemoveAll(dirPath)
56+
})
57+
4458
}
4559

4660
func generateFile(t *testing.T) string {
47-
filePath, err := ioutil.TempFile("", "file-*.xml")
61+
filePath, err := os.CreateTemp("", "file-*.xml")
4862
if err != nil {
4963
t.Errorf("Failed to create temporary file: %v", err)
5064
}
@@ -53,22 +67,116 @@ func generateFile(t *testing.T) string {
5367
}
5468

5569
func generateDir(t *testing.T) string {
56-
dirPath, err := ioutil.TempDir("", "")
70+
return generateDirWithFilesAndNestedDir(t, 5, 3)
71+
}
72+
73+
func generateDirWithFilesAndNestedDir(t *testing.T, fNumber, dirNumber int) string {
74+
dirPath, err := os.MkdirTemp("", "")
5775
assert.Nil(t, err)
5876

59-
nestedDir, err := ioutil.TempDir(dirPath, "xml-*")
77+
nestedDir, err := os.MkdirTemp(dirPath, "xml-*")
6078
assert.Nil(t, err)
6179

62-
for i := 0; i < 5; i++ {
63-
_, err = ioutil.TempFile(nestedDir, "file-*.xml")
80+
for i := 0; i < fNumber; i++ {
81+
_, err = os.CreateTemp(nestedDir, "file-*.xml")
6482
assert.Nil(t, err)
6583
}
6684

67-
nestedDir, _ = ioutil.TempDir(dirPath, "json-*")
68-
for i := 0; i < 3; i++ {
69-
_, err := ioutil.TempFile(nestedDir, "file-*.json")
85+
nestedDir, _ = os.MkdirTemp(dirPath, "json-*")
86+
for i := 0; i < dirNumber; i++ {
87+
_, err := os.MkdirTemp(nestedDir, "file-*.json")
7088
assert.Nil(t, err)
7189
}
7290

7391
return dirPath
7492
}
93+
94+
func TestWriteToTmpFile(t *testing.T) {
95+
tr := parser.TestResults{
96+
ID: "1234",
97+
Name: "Test",
98+
Framework: "JUnit",
99+
IsDisabled: false,
100+
Suites: nil,
101+
Summary: parser.Summary{
102+
Total: 10,
103+
Passed: 5,
104+
Skipped: 0,
105+
Error: 0,
106+
Failed: 5,
107+
Disabled: 0,
108+
Duration: 360,
109+
},
110+
Status: "OK",
111+
StatusMessage: "Test",
112+
}
113+
result := parser.Result{TestResults: []parser.TestResults{tr}}
114+
jsonData, _ := json.Marshal(&result)
115+
116+
t.Run("Write to one tmp file", func(t *testing.T) {
117+
file, err := cli.WriteToTmpFile(jsonData)
118+
assert.NoError(t, err)
119+
os.Remove(file)
120+
})
121+
122+
t.Run("Write to three thousand tmp files", func(t *testing.T) {
123+
fileNumber := 3000
124+
files := make([]string, 0, fileNumber)
125+
126+
for i := 0; i < fileNumber; i++ {
127+
file, err := cli.WriteToTmpFile(jsonData)
128+
assert.NoError(t, err)
129+
130+
files = append(files, file)
131+
}
132+
133+
for _, file := range files {
134+
os.Remove(file)
135+
}
136+
})
137+
}
138+
139+
func TestWriteToFile(t *testing.T) {
140+
tr := parser.TestResults{
141+
ID: "1234",
142+
Name: "Test",
143+
Framework: "JUnit",
144+
IsDisabled: false,
145+
Suites: nil,
146+
Summary: parser.Summary{
147+
Total: 10,
148+
Passed: 5,
149+
Skipped: 0,
150+
Error: 0,
151+
Failed: 5,
152+
Disabled: 0,
153+
Duration: 360,
154+
},
155+
Status: "OK",
156+
StatusMessage: "Test",
157+
}
158+
result := parser.Result{TestResults: []parser.TestResults{tr}}
159+
jsonData, _ := json.Marshal(&result)
160+
161+
t.Run("Write to one file", func(t *testing.T) {
162+
file, err := cli.WriteToFile(jsonData, "out")
163+
assert.NoError(t, err)
164+
os.Remove(file)
165+
})
166+
167+
t.Run("Write to three thousand files", func(t *testing.T) {
168+
fileNumber := 3000
169+
dirPath, err := os.MkdirTemp("", "test-results-*")
170+
require.NoError(t, err)
171+
172+
defer os.RemoveAll(dirPath)
173+
174+
for i := 0; i < fileNumber; i++ {
175+
tmpFile, err := os.CreateTemp(dirPath, "result-*.json")
176+
require.NoError(t, err)
177+
178+
_, err = cli.WriteToFile(jsonData, tmpFile.Name())
179+
require.NoError(t, err)
180+
}
181+
})
182+
}

pkg/fileloader/fileloader.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package fileloader
33
import (
44
"bytes"
55
"io"
6-
"io/ioutil"
6+
"os"
77

88
"github.com/semaphoreci/test-results/pkg/logger"
99
)
@@ -17,19 +17,23 @@ func Load(path string, reader *bytes.Reader) (*bytes.Reader, bool) {
1717

1818
// Ensure puts reader data into temporary created file.
1919
func Ensure(reader *bytes.Reader) (fileName string) {
20-
file, err := ioutil.TempFile("", "")
20+
file, err := os.CreateTemp("", "")
2121
if err != nil {
2222
panic(err)
2323
}
2424

25+
defer file.Close() // #nosec
26+
2527
fileName = file.Name()
2628

2729
_, err = reader.WriteTo(file)
2830
if err != nil {
2931
panic(err)
3032
}
3133

32-
defer file.Close() // #nosec
34+
if err = file.Sync(); err != nil {
35+
panic(err)
36+
}
3337

3438
return
3539
}

0 commit comments

Comments
 (0)