From 1a3c3898c12d4a3d315d8460bb48617e8ef59c2c Mon Sep 17 00:00:00 2001 From: Anil Belur Date: Sun, 29 Nov 2015 09:08:03 +0530 Subject: [PATCH] Fixes #1931 Now this change ensures `config.json` is first written into a temp-file and rename into the original file, if no errors from the FS. If ENOSPC occurs on write, when the machine is restarted, the previous `config.json` is still available on startup. Extended `TestStoreSave()` to check for any residual config files in the machine directory. Signed-off-by: Anil Belur --- libmachine/persist/filestore.go | 26 +++++++++++++++++++++++++- libmachine/persist/filestore_test.go | 12 ++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/libmachine/persist/filestore.go b/libmachine/persist/filestore.go index 739e9b5fc8..62263dfe8a 100644 --- a/libmachine/persist/filestore.go +++ b/libmachine/persist/filestore.go @@ -31,7 +31,31 @@ func (s Filestore) GetMachinesDir() string { } func (s Filestore) saveToFile(data []byte, file string) error { - return ioutil.WriteFile(file, data, 0600) + if _, err := os.Stat(file); os.IsNotExist(err) { + return ioutil.WriteFile(file, data, 0600) + } + + tmpfi, err := ioutil.TempFile(filepath.Dir(file), "config.json.tmp") + if err != nil { + return err + } + defer os.Remove(tmpfi.Name()) + + err = ioutil.WriteFile(tmpfi.Name(), data, 0600) + if err != nil { + return err + } + + err = os.Remove(file) + if err != nil { + return err + } + + err = os.Rename(tmpfi.Name(), file) + if err != nil { + return err + } + return nil } func (s Filestore) Save(host *host.Host) error { diff --git a/libmachine/persist/filestore_test.go b/libmachine/persist/filestore_test.go index 9928f89a3b..874ecda43b 100644 --- a/libmachine/persist/filestore_test.go +++ b/libmachine/persist/filestore_test.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "os" "path/filepath" + "regexp" "testing" "github.com/docker/machine/commands/mcndirs" @@ -52,6 +53,17 @@ func TestStoreSave(t *testing.T) { if _, err := os.Stat(path); os.IsNotExist(err) { t.Fatalf("Host path doesn't exist: %s", path) } + + files, _ := ioutil.ReadDir(path) + for _, f := range files { + r, err := regexp.Compile("config.json.tmp*") + if err != nil { + t.Fatalf("Failed to compile regexp string") + } + if r.MatchString(f.Name()) { + t.Fatalf("Failed to remove temp filestore:%s", f.Name()) + } + } } func TestStoreSaveOmitRawDriver(t *testing.T) {