Allow nodeup (and others) to replace in-use files

By creating a tempfile and then moving the file into place, we both
write more atomically and we can overwrite in-use files.

Issue #10122
This commit is contained in:
Justin SB 2021-01-14 10:52:10 -05:00
parent e109c9c583
commit acb247fa5f
1 changed files with 36 additions and 8 deletions

View File

@ -19,8 +19,10 @@ package fi
import ( import (
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"path" "path"
"path/filepath"
"strconv" "strconv"
"k8s.io/klog/v2" "k8s.io/klog/v2"
@ -49,22 +51,48 @@ func WriteFile(destPath string, contents Resource, fileMode os.FileMode, dirMode
func writeFileContents(destPath string, src Resource, fileMode os.FileMode) error { func writeFileContents(destPath string, src Resource, fileMode os.FileMode) error {
klog.Infof("Writing file %q", destPath) klog.Infof("Writing file %q", destPath)
out, err := os.OpenFile(destPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, fileMode)
if err != nil {
return fmt.Errorf("error opening destination file %q: %v", destPath, err)
}
defer out.Close()
in, err := src.Open() in, err := src.Open()
if err != nil { if err != nil {
return fmt.Errorf("error opening source resource for file %q: %v", destPath, err) return fmt.Errorf("error opening source resource for file %q: %v", destPath, err)
} }
defer SafeClose(in) defer SafeClose(in)
_, err = io.Copy(out, in) dir := filepath.Dir(destPath)
tempFile, err := ioutil.TempFile(dir, ".writefile")
if err != nil { if err != nil {
return fmt.Errorf("error writing file %q: %v", destPath, err) return fmt.Errorf("error creating temp file in %q: %w", dir, err)
} }
closeTempFile := true
deleteTempFile := true
defer func() {
if closeTempFile {
if err := tempFile.Close(); err != nil {
klog.Warningf("error closing tempfile %q: %v", tempFile.Name(), err)
}
}
if deleteTempFile {
if err := os.Remove(tempFile.Name()); err != nil {
klog.Warningf("error removing tempfile %q: %v", tempFile.Name(), err)
}
}
}()
if _, err := io.Copy(tempFile, in); err != nil {
return fmt.Errorf("error writing file %q: %v", tempFile.Name(), err)
}
if err := tempFile.Close(); err != nil {
return fmt.Errorf("error closing temp file %q: %w", tempFile.Name(), err)
}
closeTempFile = false
if err := os.Rename(tempFile.Name(), destPath); err != nil {
return fmt.Errorf("error renaming temp file %q -> %q: %w", tempFile.Name(), destPath, err)
}
deleteTempFile = false
return nil return nil
} }