Make the charts cache safe in presence of several Helm instances

If several instances of Helm are run at the same moment and try to download the
same chart, some of them might see an empty or incomplete file in cache. Prevent
that by saving the dowloaded file atomically.

Closes #7600

Signed-off-by: Mikhail Gusarov <misha@ridge.co>
pull/7720/head
Mikhail Gusarov 5 years ago
parent 13ffa52a59
commit b864fcd0f4

@ -72,6 +72,31 @@ type ChartDownloader struct {
RepositoryCache string
}
// atomicWriteFile atomically (as atomic as os.Rename allows) writes a file to a
// disk.
func atomicWriteFile(filename string, body io.Reader, mode os.FileMode) error {
tempFile, err := ioutil.TempFile(filepath.Split(filename))
if err != nil {
return err
}
tempName := tempFile.Name()
if _, err := io.Copy(tempFile, body); err != nil {
tempFile.Close() // return value is ignored as we are already on error path
return err
}
if err := tempFile.Close(); err != nil {
return err
}
if err := os.Chmod(tempName, mode); err != nil {
return err
}
return os.Rename(tempName, filename)
}
// DownloadTo retrieves a chart. Depending on the settings, it may also download a provenance file.
//
// If Verify is set to VerifyNever, the verification will be nil.
@ -101,7 +126,7 @@ func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *proven
name := filepath.Base(u.Path)
destfile := filepath.Join(dest, name)
if err := ioutil.WriteFile(destfile, data.Bytes(), 0644); err != nil {
if err := atomicWriteFile(destfile, data, 0644); err != nil {
return destfile, nil, err
}
@ -117,7 +142,7 @@ func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *proven
return destfile, ver, nil
}
provfile := destfile + ".prov"
if err := ioutil.WriteFile(provfile, body.Bytes(), 0644); err != nil {
if err := atomicWriteFile(provfile, body, 0644); err != nil {
return destfile, nil, err
}

Loading…
Cancel
Save