diff --git a/pkg/client/unversioned/clientcmd/loader.go b/pkg/client/unversioned/clientcmd/loader.go index f0c9c547a1d..3d2df1f7da3 100644 --- a/pkg/client/unversioned/clientcmd/loader.go +++ b/pkg/client/unversioned/clientcmd/loader.go @@ -381,12 +381,38 @@ func WriteToFile(config clientcmdapi.Config, filename string) error { return err } } + + err = lockFile(filename) + if err != nil { + return err + } + defer unlockFile(filename) + if err := ioutil.WriteFile(filename, content, 0600); err != nil { return err } return nil } +func lockFile(filename string) error { + // TODO: find a way to do this with actual file locks. Will + // probably need seperate solution for windows and linux. + f, err := os.OpenFile(lockName(filename), os.O_CREATE|os.O_EXCL, 0) + if err != nil { + return err + } + f.Close() + return nil +} + +func unlockFile(filename string) error { + return os.Remove(lockName(filename)) +} + +func lockName(filename string) string { + return filename + ".lock" +} + // Write serializes the config to yaml. // Encapsulates serialization without assuming the destination is a file. func Write(config clientcmdapi.Config) ([]byte, error) { diff --git a/pkg/client/unversioned/clientcmd/loader_test.go b/pkg/client/unversioned/clientcmd/loader_test.go index ad79c7b8183..5075bded221 100644 --- a/pkg/client/unversioned/clientcmd/loader_test.go +++ b/pkg/client/unversioned/clientcmd/loader_test.go @@ -376,6 +376,22 @@ func TestMigratingFileSourceMissingSkip(t *testing.T) { } } +func TestFileLocking(t *testing.T) { + f, _ := ioutil.TempFile("", "") + defer os.Remove(f.Name()) + + err := lockFile(f.Name()) + if err != nil { + t.Errorf("unexpected error while locking file: %v", err) + } + defer unlockFile(f.Name()) + + err = lockFile(f.Name()) + if err == nil { + t.Error("expected error while locking file.") + } +} + func Example_noMergingOnExplicitPaths() { commandLineFile, _ := ioutil.TempFile("", "") defer os.Remove(commandLineFile.Name())