Skip to content

Commit 3eec49b

Browse files
authored
Fix race condition in config handling (main) (#2937)
* Ignore non-existent files when deleting temp-files * Only delete files older than 5min
1 parent e9b1f00 commit 3eec49b

File tree

3 files changed

+60
-14
lines changed

3 files changed

+60
-14
lines changed

integration/shared/isolated/config_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package isolated
22

33
import (
44
"io/ioutil"
5+
"os"
56
"path/filepath"
7+
"time"
68

79
helpers "code.cloudfoundry.org/cli/integration/helpers"
810
. "github.com/onsi/ginkgo/v2"
@@ -43,6 +45,9 @@ var _ = Describe("Config", func() {
4345
tmpFile, err := ioutil.TempFile(configDir, "temp-config")
4446
Expect(err).ToNot(HaveOccurred())
4547
tmpFile.Close()
48+
oldTime := time.Now().Add(-time.Minute * 10)
49+
err = os.Chtimes(tmpFile.Name(), oldTime, oldTime)
50+
Expect(err).ToNot(HaveOccurred())
4651
}
4752
})
4853

util/configv3/load_config.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ package configv3
22

33
import (
44
"encoding/json"
5+
"errors"
56
"io/ioutil"
67
"math"
78
"os"
89
"path/filepath"
10+
"time"
911

1012
"code.cloudfoundry.org/cli/command/translatableerror"
1113
"golang.org/x/crypto/ssh/terminal"
@@ -182,8 +184,20 @@ func removeOldTempConfigFiles() error {
182184
}
183185

184186
for _, oldTempFileName := range oldTempFileNames {
185-
err = os.Remove(oldTempFileName)
187+
fi, err := os.Lstat(oldTempFileName)
186188
if err != nil {
189+
// ignore if file doesn't exist anymore due to race conditions if multiple cli commands are running in parallel
190+
if errors.Is(err, os.ErrNotExist) {
191+
continue
192+
}
193+
return err
194+
}
195+
// only delete old orphans which are not caught by the signal handler in WriteConfig
196+
if fi.ModTime().After(time.Now().Add(-5 * time.Minute)) {
197+
continue
198+
}
199+
err = os.Remove(oldTempFileName)
200+
if err != nil && !errors.Is(err, os.ErrNotExist) {
187201
return err
188202
}
189203
}

util/configv3/load_config_test.go

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"io/ioutil"
66
"os"
77
"path/filepath"
8+
"time"
89

910
"code.cloudfoundry.org/cli/command/translatableerror"
1011
"code.cloudfoundry.org/cli/integration/helpers"
@@ -60,22 +61,48 @@ var _ = Describe("Config", func() {
6061
})
6162

6263
When("there are old temp-config* files lingering from previous failed attempts to write the config", func() {
63-
BeforeEach(func() {
64-
configDir := filepath.Join(homeDir, ".cf")
65-
Expect(os.MkdirAll(configDir, 0777)).To(Succeed())
66-
for i := 0; i < 3; i++ {
67-
tmpFile, fileErr := ioutil.TempFile(configDir, "temp-config")
68-
Expect(fileErr).ToNot(HaveOccurred())
69-
tmpFile.Close()
70-
}
64+
Context("and the files are younger than 5 minutes", func() {
65+
BeforeEach(func() {
66+
configDir := filepath.Join(homeDir, ".cf")
67+
Expect(os.MkdirAll(configDir, 0777)).To(Succeed())
68+
for i := 0; i < 3; i++ {
69+
configDir := filepath.Join(homeDir, ".cf")
70+
tmpFile, fileErr := ioutil.TempFile(configDir, "temp-config")
71+
Expect(fileErr).ToNot(HaveOccurred())
72+
tmpFile.Close()
73+
}
74+
})
75+
76+
It("keeps the files", func() {
77+
Expect(loadErr).ToNot(HaveOccurred())
78+
79+
oldTempFileNames, configErr := filepath.Glob(filepath.Join(homeDir, ".cf", "temp-config?*"))
80+
Expect(configErr).ToNot(HaveOccurred())
81+
Expect(oldTempFileNames).To(HaveLen(3))
82+
})
7183
})
7284

73-
It("removes the lingering temp-config* files", func() {
74-
Expect(loadErr).ToNot(HaveOccurred())
85+
Context("and the files are older than 5 minutes", func() {
86+
BeforeEach(func() {
87+
configDir := filepath.Join(homeDir, ".cf")
88+
Expect(os.MkdirAll(configDir, 0777)).To(Succeed())
89+
for i := 0; i < 3; i++ {
90+
tmpFile, fileErr := ioutil.TempFile(configDir, "temp-config")
91+
Expect(fileErr).ToNot(HaveOccurred())
92+
tmpFile.Close()
93+
oldTime := time.Now().Add(-time.Minute * 10)
94+
err := os.Chtimes(tmpFile.Name(), oldTime, oldTime)
95+
Expect(err).ToNot(HaveOccurred())
96+
}
97+
})
7598

76-
oldTempFileNames, configErr := filepath.Glob(filepath.Join(homeDir, ".cf", "temp-config?*"))
77-
Expect(configErr).ToNot(HaveOccurred())
78-
Expect(oldTempFileNames).To(BeEmpty())
99+
It("removes the lingering temp-config* files", func() {
100+
Expect(loadErr).ToNot(HaveOccurred())
101+
102+
oldTempFileNames, configErr := filepath.Glob(filepath.Join(homeDir, ".cf", "temp-config?*"))
103+
Expect(configErr).ToNot(HaveOccurred())
104+
Expect(oldTempFileNames).To(BeEmpty())
105+
})
79106
})
80107
})
81108

0 commit comments

Comments
 (0)