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>
Signed-off-by: Matheus Hunsche <matheus.hunsche@ifood.com.br>
pull/8840/head
Mikhail Gusarov 6 years ago committed by Matheus Hunsche
parent 5f28d63c57
commit cac8a32cca

@ -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