diff --git a/cmd/template.go b/cmd/template.go index e335ff646b2a3f1bbd897d89e94c57332727257a..5025684225c93704bea349c67fa206ff153312a4 100644 --- a/cmd/template.go +++ b/cmd/template.go @@ -18,7 +18,6 @@ package cmd import ( "errors" - "os" "runtime" "strings" @@ -71,7 +70,7 @@ func createTemplate(cmd *cobra.Command, args []string) error { output = output + "/" } - if err := os.WriteFile(output+"template.yaml", data, utils.DeployConfigFileMode); err != nil { + if err := utils.AtomicWriteFile(output+"template.yaml", data, utils.DeployConfigFileMode); err != nil { logrus.Errorf("Faild to write template config file: %v", err) return err } diff --git a/pkg/utils/filehandler.go b/pkg/utils/filehandler.go index b6aca2b91f54793b520775a88c07079f3df4680c..685008967198f6c158a94d8804a30037482f4cf6 100644 --- a/pkg/utils/filehandler.go +++ b/pkg/utils/filehandler.go @@ -18,6 +18,7 @@ package utils import ( "bytes" "io" + "os" "path/filepath" "strings" "text/template" @@ -76,3 +77,29 @@ func applyTmplData(tmpl *template.Template, data interface{}) string { } return buf.String() } + +// AtomicWriteFile writes data to filename atomically. +// It creates a temp file in the same directory, writes data, then renames it. +func AtomicWriteFile(filename string, data []byte, perm os.FileMode) error { + tmpFile, err := os.CreateTemp(filepath.Dir(filename), ".tmp.*") + if err != nil { + return err + } + defer os.Remove(tmpFile.Name()) // cleanup on failure + + if _, err := tmpFile.Write(data); err != nil { + tmpFile.Close() + return err + } + + if err := tmpFile.Chmod(perm); err != nil { + tmpFile.Close() + return err + } + + if err := tmpFile.Close(); err != nil { + return err + } + + return os.Rename(tmpFile.Name(), filename) +} \ No newline at end of file